<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Represents a rust struct member
struct Member {
    name: String,
    value: String,
    offset: Option<usize>
}

impl Member {
    fn declaration_text(&self) -> String {
        let offset = match self.offset {
            Some(o) => format!("/* offset: {} */", o),
            _ => "".to_owned(),
        };
        format!("    pub {}: {} {}", self.name, self.value, offset)
    }
    fn copy_text(&self) -> String {
        format!("            {name}: self.{name}", name = self.name)
    }
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    // The members of this struct.
    let mut rust_members = Vec::with_capacity(members.len());

    // Padding structs will be named `_paddingN` where `N` is determined by this variable.
    let mut next_padding_num = 0;

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        // Compute infos about the member.
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        // Finding offset of the current member, as requested by the SPIR-V code.
        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                let padding_num = next_padding_num; next_padding_num += 1;
                rust_members.push(Member {
                        name: format!("_dummy{}", padding_num),
                        value: format!("[u8; {}]", diff),
                        offset: None,
                });
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        rust_members.push(Member {
                name: member_name.to_owned(),
                value: ty,
                offset: Some(spirv_offset),
        });
    }

    // Try determine the total size of the struct in order to add padding at the end of the struct.
    let spirv_req_total_size = doc.instructions.iter().filter_map(|i| {
        match *i {
            parse::Instruction::Decorate { target_id,
                                           decoration: enums::Decoration::DecorationArrayStride,
                                           ref params } =>
            {
                for inst in doc.instructions.iter() {
                    match *inst {
                        parse::Instruction::TypeArray { result_id, type_id, .. }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        parse::Instruction::TypeRuntimeArray { result_id, type_id }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        _ => ()
                    }
                }

                None
            },
            _ => None
        }
    }).fold(None, |a, b| if let Some(a) = a { assert_eq!(a, b); Some(a) } else { Some(b) });

    // Adding the final padding members.
    if let (Some(cur_size), Some(req_size)) = (current_rust_offset, spirv_req_total_size) {
        let diff = req_size.checked_sub(cur_size as u32).unwrap();
        if diff >= 1 {
            rust_members.push(Member {
                    name: format!("_dummy{}", next_padding_num),
                    value: format!("[u8; {}]", diff),
                    offset: None,
            });
        }
    }

    // We can only implement Clone if there's no unsized member in the struct.
    let impl_text = if current_rust_offset.is_some() {
        format!("\nimpl Clone for {name} {{\n    fn clone(&self) -> Self {{\n        \
                 {name} {{\n{copies}\n        }}\n    }}\n}}\n", name = name,
                 copies = rust_members.iter().map(Member::copy_text).collect::<Vec<_>>().join(",\n"))
    } else {
        "".to_owned()
    };

    format!("#[repr(C)]\npub struct {name} {{\n{members}\n}} /* total_size: {t:?} */\n{impl_text}",
            name = name,
            members = rust_members.iter().map(Member::declaration_text).collect::<Vec<_>>().join(",\n"),
            t = spirv_req_total_size, impl_text=impl_text)
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                #[repr(C)] struct Foo { data: bool, after: u8 }
                let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                return ("bool".to_owned(), Some(size), mem::align_of::<Foo>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                match (width, signedness) {
                    (8, true) => {
                        #[repr(C)] struct Foo { data: i8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (8, false) => {
                        #[repr(C)] struct Foo { data: u8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, true) => {
                        #[repr(C)] struct Foo { data: i16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, false) => {
                        #[repr(C)] struct Foo { data: u16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, true) => {
                        #[repr(C)] struct Foo { data: i32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, false) => {
                        #[repr(C)] struct Foo { data: u32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, true) => {
                        #[repr(C)] struct Foo { data: i64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, false) => {
                        #[repr(C)] struct Foo { data: u64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for an integer of width {}", width)
                }
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                match width {
                    32 => {
                        #[repr(C)] struct Foo { data: f32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    64 => {
                        #[repr(C)] struct Foo { data: f64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for a floating-point of width {}", width)
                }
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                // TODO: take the Offset member decorate into account?
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<commit_msg>Derive copy on generated structs when possible<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;

use parse;
use enums;

/// Translates all the structs that are contained in the SPIR-V document as Rust structs.
pub fn write_structs(doc: &parse::Spirv) -> String {
    let mut result = String::new();

    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::TypeStruct { result_id, ref member_types } => {
                result.push_str(&write_struct(doc, result_id, member_types));
                result.push_str("\n");
            },
            _ => ()
        }
    }

    result
}

/// Represents a rust struct member
struct Member {
    name: String,
    value: String,
    offset: Option<usize>
}

impl Member {
    fn declaration_text(&self) -> String {
        let offset = match self.offset {
            Some(o) => format!("/* offset: {} */", o),
            _ => "".to_owned(),
        };
        format!("    pub {}: {} {}", self.name, self.value, offset)
    }
    fn copy_text(&self) -> String {
        format!("            {name}: self.{name}", name = self.name)
    }
}

/// Writes a single struct.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> String {
    let name = ::name_from_id(doc, struct_id);

    // The members of this struct.
    let mut rust_members = Vec::with_capacity(members.len());

    // Padding structs will be named `_paddingN` where `N` is determined by this variable.
    let mut next_padding_num = 0;

    // Contains the offset of the next field.
    // Equals to `None` if there's a runtime-sized field in there.
    let mut current_rust_offset = Some(0);

    for (num, &member) in members.iter().enumerate() {
        // Compute infos about the member.
        let (ty, rust_size, rust_align) = type_from_id(doc, member);
        let member_name = ::member_name_from_id(doc, struct_id, num as u32);

        // Ignore the whole struct is a member is built in, which includes
        // `gl_Position` for example.
        if is_builtin_member(doc, struct_id, num as u32) {
            return String::new();
        }

        // Finding offset of the current member, as requested by the SPIR-V code.
        let spirv_offset = doc.instructions.iter().filter_map(|i| {
            match *i {
                parse::Instruction::MemberDecorate { target_id, member,
                                                   decoration: enums::Decoration::DecorationOffset,
                                                   ref params } if target_id == struct_id &&
                                                                   member as usize == num =>
                {
                    return Some(params[0]);
                },
                _ => ()
            };

            None
        }).next();

        // Some structs don't have `Offset` decorations, in the case they are used as local
        // variables only. Ignoring these.
        let spirv_offset = match spirv_offset {
            Some(o) => o as usize,
            None => return String::new()
        };

        // We need to add a dummy field if necessary.
        {
            let current_rust_offset = current_rust_offset.as_mut().expect("Found runtime-sized member in non-final position");

            // Updating current_rust_offset to take the alignment of the next field into account
            *current_rust_offset = if *current_rust_offset == 0 {
                0
            } else {
                (1 + (*current_rust_offset - 1) / rust_align) * rust_align
            };

            if spirv_offset != *current_rust_offset {
                let diff = spirv_offset.checked_sub(*current_rust_offset).unwrap();
                let padding_num = next_padding_num; next_padding_num += 1;
                rust_members.push(Member {
                        name: format!("_dummy{}", padding_num),
                        value: format!("[u8; {}]", diff),
                        offset: None,
                });
                *current_rust_offset += diff;
            }
        }

        // Updating `current_rust_offset`.
        if let Some(s) = rust_size {
            *current_rust_offset.as_mut().unwrap() += s;
        } else {
            current_rust_offset = None;
        }

        rust_members.push(Member {
                name: member_name.to_owned(),
                value: ty,
                offset: Some(spirv_offset),
        });
    }

    // Try determine the total size of the struct in order to add padding at the end of the struct.
    let spirv_req_total_size = doc.instructions.iter().filter_map(|i| {
        match *i {
            parse::Instruction::Decorate { target_id,
                                           decoration: enums::Decoration::DecorationArrayStride,
                                           ref params } =>
            {
                for inst in doc.instructions.iter() {
                    match *inst {
                        parse::Instruction::TypeArray { result_id, type_id, .. }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        parse::Instruction::TypeRuntimeArray { result_id, type_id }
                            if result_id == target_id && type_id == struct_id =>
                        {
                            return Some(params[0]);
                        },
                        _ => ()
                    }
                }

                None
            },
            _ => None
        }
    }).fold(None, |a, b| if let Some(a) = a { assert_eq!(a, b); Some(a) } else { Some(b) });

    // Adding the final padding members.
    if let (Some(cur_size), Some(req_size)) = (current_rust_offset, spirv_req_total_size) {
        let diff = req_size.checked_sub(cur_size as u32).unwrap();
        if diff >= 1 {
            rust_members.push(Member {
                    name: format!("_dummy{}", next_padding_num),
                    value: format!("[u8; {}]", diff),
                    offset: None,
            });
        }
    }

    // We can only implement Clone if there's no unsized member in the struct.
    let (impl_text, derive_text) = if current_rust_offset.is_some() {
        let i = format!("\nimpl Clone for {name} {{\n    fn clone(&self) -> Self {{\n        \
                 {name} {{\n{copies}\n        }}\n    }}\n}}\n", name = name,
                 copies = rust_members.iter().map(Member::copy_text).collect::<Vec<_>>().join(",\n"));
        (i, "#[derive(Copy)]")
    } else {
        ("".to_owned(), "")
    };

    format!("#[repr(C)]{derive_text}\npub struct {name} {{\n{members}\n}} /* total_size: {t:?} */\n{impl_text}",
            name = name,
            members = rust_members.iter().map(Member::declaration_text).collect::<Vec<_>>().join(",\n"),
            t = spirv_req_total_size, impl_text = impl_text, derive_text = derive_text)
}

/// Returns true if a `BuiltIn` decorator is applied on a struct member.
fn is_builtin_member(doc: &parse::Spirv, id: u32, member_id: u32) -> bool {
    for instruction in &doc.instructions {
        match *instruction {
            parse::Instruction::MemberDecorate { target_id, member,
                                                 decoration: enums::Decoration::DecorationBuiltIn,
                                                 .. } if target_id == id && member == member_id =>
            {
                return true;
            },
            _ => ()
        }
    }

    false
}

/// Returns the type name to put in the Rust struct, and its size and alignment.
///
/// The size can be `None` if it's only known at runtime.
pub fn type_from_id(doc: &parse::Spirv, searched: u32) -> (String, Option<usize>, usize) {
    for instruction in doc.instructions.iter() {
        match instruction {
            &parse::Instruction::TypeBool { result_id } if result_id == searched => {
                #[repr(C)] struct Foo { data: bool, after: u8 }
                let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                return ("bool".to_owned(), Some(size), mem::align_of::<Foo>())
            },
            &parse::Instruction::TypeInt { result_id, width, signedness } if result_id == searched => {
                match (width, signedness) {
                    (8, true) => {
                        #[repr(C)] struct Foo { data: i8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (8, false) => {
                        #[repr(C)] struct Foo { data: u8, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u8".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, true) => {
                        #[repr(C)] struct Foo { data: i16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (16, false) => {
                        #[repr(C)] struct Foo { data: u16, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u16".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, true) => {
                        #[repr(C)] struct Foo { data: i32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (32, false) => {
                        #[repr(C)] struct Foo { data: u32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, true) => {
                        #[repr(C)] struct Foo { data: i64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("i64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    (64, false) => {
                        #[repr(C)] struct Foo { data: u64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("u64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for an integer of width {}", width)
                }
            },
            &parse::Instruction::TypeFloat { result_id, width } if result_id == searched => {
                match width {
                    32 => {
                        #[repr(C)] struct Foo { data: f32, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f32".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    64 => {
                        #[repr(C)] struct Foo { data: f64, after: u8 }
                        let size = unsafe { (&(&*(0 as *const Foo)).after) as *const u8 as usize };
                        return ("f64".to_owned(), Some(size), mem::align_of::<Foo>())
                    },
                    _ => panic!("No Rust equivalent for a floating-point of width {}", width)
                }
            },
            &parse::Instruction::TypeVector { result_id, component_id, count } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, component_id);
                return (format!("[{}; {}]", t, count), t_size.map(|s| s * count as usize), t_align);
            },
            &parse::Instruction::TypeMatrix { result_id, column_type_id, column_count } if result_id == searched => {
                // FIXME: row-major or column-major
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, column_type_id);
                return (format!("[{}; {}]", t, column_count), t_size.map(|s| s * column_count as usize), t_align);
            },
            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, t_size, t_align) = type_from_id(doc, type_id);
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                return (format!("[{}; {}]", t, len), t_size.map(|s| s * len as usize), t_align);       // FIXME:
            },
            &parse::Instruction::TypeRuntimeArray { result_id, type_id } if result_id == searched => {
                debug_assert_eq!(mem::align_of::<[u32; 3]>(), mem::align_of::<u32>());
                let (t, _, t_align) = type_from_id(doc, type_id);
                return (format!("[{}]", t), None, t_align);
            },
            &parse::Instruction::TypeStruct { result_id, ref member_types } if result_id == searched => {
                // TODO: take the Offset member decorate into account?
                let name = ::name_from_id(doc, result_id);
                let size = member_types.iter().filter_map(|&t| type_from_id(doc, t).1).fold(0, |a,b|a+b);
                let align = member_types.iter().map(|&t| type_from_id(doc, t).2).max().unwrap_or(1);
                return (name, Some(size), align);
            },
            _ => ()
        }
    }

    panic!("Type #{} not found", searched)
}
<|endoftext|>


<commit_before>//! A simple communication infrastructure providing typed exchange channels.
//!
//! This crate is part of the timely dataflow system, used primarily for its inter-worker communication.
//! It may be indepedently useful, but it is separated out mostly to make clear boundaries in the project.
//!
//! Threads are spawned with an [`allocator::Generic`](./allocator/generic/enum.Generic.html), whose `allocate` method returns a pair of several send endpoints and one
//! receive endpoint. Messages sent into a send endpoint will eventually be received by the corresponding worker,
//! if it receives often enough. The point-to-point channels are each FIFO, but with no fairness guarantees.
//!
//! To be communicated, a type must implement the [`Serialize`](./trait.Serialize.html) trait. A default implementation of `Serialize` is
//! provided for any type implementing [`Abomonation`](../abomonation/trait.Abomonation.html). To implement other serialization strategies, wrap your type
//! and implement `Serialize` for your wrapper.
//!
//! Channel endpoints also implement a lower-level `push` and `pull` interface (through the [`Push`](./trait.Push.html) and [`Pull`](./trait.Pull.html)
//! traits), which is used for more precise control of resources.
//!
//! #Examples
//! ```
//! // configure for two threads, just one process.
//! let config = timely_communication::Configuration::Process(2);
//!
//! // initializes communication, spawns workers
//! let guards = timely_communication::initialize(config, |mut allocator| {
//!     println!("worker {} started", allocator.index());
//!
//!     // allocates pair of senders list and one receiver.
//!     let (mut senders, mut receiver) = allocator.allocate();
//!
//!     // send typed data along each channel
//!     senders[0].send(format!("hello, {}", 0));
//!     senders[1].send(format!("hello, {}", 1));
//!
//!     // no support for termination notification,
//!     // we have to count down ourselves.
//!     let mut expecting = 2;
//!     while expecting > 0 {
//!         if let Some(message) = receiver.recv() {
//!             println!("worker {}: received: <{}>", allocator.index(), message);
//!             expecting -= 1;
//!         }
//!     }
//!
//!     // optionally, return something
//!     allocator.index()
//! });
//!
//! // computation runs until guards are joined or dropped.
//! if let Ok(guards) = guards {
//!     for guard in guards.join() {
//!         println!("result: {:?}", guard);
//!     }
//! }
//! else { println!("error in computation"); }
//! ```
//!
//! The should produce output like:
//!
//! ```ignore
//! worker 0 started
//! worker 1 started
//! worker 0: received: <hello, 0>
//! worker 1: received: <hello, 1>
//! worker 0: received: <hello, 0>
//! worker 1: received: <hello, 1>
//! result: Ok(0)
//! result: Ok(1)
//! ```

extern crate getopts;
extern crate byteorder;
extern crate abomonation;

pub mod allocator;
mod networking;
pub mod initialize;
mod drain;

use std::any::Any;
use abomonation::{Abomonation, encode, decode};

pub use allocator::Generic as Allocator;
pub use allocator::Allocate;
pub use initialize::{initialize, Configuration, WorkerGuards};

/// A composite trait for types that may be used with channels.
pub trait Data : Send+Any+Serialize+Clone+'static { }
impl<T: Clone+Send+Any+Serialize+'static> Data for T { }

/// Conversions to and from `Vec<u8>`.
///
/// A type must implement this trait to move along the channels produced by an `A: Allocate`.
///
/// A default implementation is provided for any `T: Abomonation+Clone`.
pub trait Serialize {
    /// Append the binary representation of `self` to a vector of bytes. The `&mut self` argument
    /// may be mutated, but the second argument should only be appended to.
    fn into_bytes(&mut self, &mut Vec<u8>);
    /// Recover an instance of Self from its binary representation. The `&mut Vec<u8>` argument may
    /// be taken with `mem::replace` if it is needed.
    fn from_bytes(&mut Vec<u8>) -> Self;
}

// NOTE : this should be unsafe, because these methods are.
// NOTE : figure this out later. don't use for serious things.
impl<T: Abomonation+Clone> Serialize for T {
    fn into_bytes(&mut self, bytes: &mut Vec<u8>) {
        unsafe { encode(self, bytes); }
    }
    fn from_bytes(bytes: &mut Vec<u8>) -> Self {
        (* unsafe { decode::<T>(bytes) }.unwrap().0).clone()
    }
}

/// Pushing elements of type `T`.
pub trait Push<T> {
    /// Pushes `element` and provides the opportunity to take ownership.
    ///
    /// The value of `element` after the call may be changed. A change does not imply anything other
    /// than that the implementor took resources associated with `element` and is returning other
    /// resources.
    fn push(&mut self, element: &mut Option<T>);
    /// Pushes `element` and drops any resulting resources.
    #[inline(always)]
    fn send(&mut self, element: T) { self.push(&mut Some(element)); }
    /// Pushes `None`, conventionally signalling a flush.
    #[inline(always)]
    fn done(&mut self) { self.push(&mut None); }
}

impl<T, P: ?Sized + Push<T>> Push<T> for Box<P> {
    #[inline(always)]
    fn push(&mut self, element: &mut Option<T>) { (**self).push(element) }
}

/// Pulling elements of type `T`.
pub trait Pull<T> {
    /// Pulls an element and provides the opportunity to take ownership.
    ///
    /// The receiver may mutate the result, in particular take ownership of the data by replacing
    /// it with other data or even `None`.
    /// If `pull` returns `None` this conventially signals that no more data is available
    /// at the moment.
    fn pull(&mut self) -> &mut Option<T>;
    /// Takes an `Option<T>` and leaves `None` behind.
    #[inline(always)]
    fn recv(&mut self) -> Option<T> { self.pull().take() }
}

impl<T, P: ?Sized + Pull<T>> Pull<T> for Box<P> {
    #[inline(always)]
    fn pull(&mut self) -> &mut Option<T> { (**self).pull() }
}
<commit_msg>remove Clone from Data<commit_after>//! A simple communication infrastructure providing typed exchange channels.
//!
//! This crate is part of the timely dataflow system, used primarily for its inter-worker communication.
//! It may be indepedently useful, but it is separated out mostly to make clear boundaries in the project.
//!
//! Threads are spawned with an [`allocator::Generic`](./allocator/generic/enum.Generic.html), whose `allocate` method returns a pair of several send endpoints and one
//! receive endpoint. Messages sent into a send endpoint will eventually be received by the corresponding worker,
//! if it receives often enough. The point-to-point channels are each FIFO, but with no fairness guarantees.
//!
//! To be communicated, a type must implement the [`Serialize`](./trait.Serialize.html) trait. A default implementation of `Serialize` is
//! provided for any type implementing [`Abomonation`](../abomonation/trait.Abomonation.html). To implement other serialization strategies, wrap your type
//! and implement `Serialize` for your wrapper.
//!
//! Channel endpoints also implement a lower-level `push` and `pull` interface (through the [`Push`](./trait.Push.html) and [`Pull`](./trait.Pull.html)
//! traits), which is used for more precise control of resources.
//!
//! #Examples
//! ```
//! // configure for two threads, just one process.
//! let config = timely_communication::Configuration::Process(2);
//!
//! // initializes communication, spawns workers
//! let guards = timely_communication::initialize(config, |mut allocator| {
//!     println!("worker {} started", allocator.index());
//!
//!     // allocates pair of senders list and one receiver.
//!     let (mut senders, mut receiver) = allocator.allocate();
//!
//!     // send typed data along each channel
//!     senders[0].send(format!("hello, {}", 0));
//!     senders[1].send(format!("hello, {}", 1));
//!
//!     // no support for termination notification,
//!     // we have to count down ourselves.
//!     let mut expecting = 2;
//!     while expecting > 0 {
//!         if let Some(message) = receiver.recv() {
//!             println!("worker {}: received: <{}>", allocator.index(), message);
//!             expecting -= 1;
//!         }
//!     }
//!
//!     // optionally, return something
//!     allocator.index()
//! });
//!
//! // computation runs until guards are joined or dropped.
//! if let Ok(guards) = guards {
//!     for guard in guards.join() {
//!         println!("result: {:?}", guard);
//!     }
//! }
//! else { println!("error in computation"); }
//! ```
//!
//! The should produce output like:
//!
//! ```ignore
//! worker 0 started
//! worker 1 started
//! worker 0: received: <hello, 0>
//! worker 1: received: <hello, 1>
//! worker 0: received: <hello, 0>
//! worker 1: received: <hello, 1>
//! result: Ok(0)
//! result: Ok(1)
//! ```

extern crate getopts;
extern crate byteorder;
extern crate abomonation;

pub mod allocator;
mod networking;
pub mod initialize;
mod drain;

use std::any::Any;
use abomonation::{Abomonation, encode, decode};

pub use allocator::Generic as Allocator;
pub use allocator::Allocate;
pub use initialize::{initialize, Configuration, WorkerGuards};

/// A composite trait for types that may be used with channels.
pub trait Data : Send+Any+Serialize+'static { }
impl<T: Send+Any+Serialize+'static> Data for T { }

/// Conversions to and from `Vec<u8>`.
///
/// A type must implement this trait to move along the channels produced by an `A: Allocate`.
///
/// A default implementation is provided for any `T: Abomonation+Clone`.
pub trait Serialize {
    /// Append the binary representation of `self` to a vector of bytes. The `&mut self` argument
    /// may be mutated, but the second argument should only be appended to.
    fn into_bytes(&mut self, &mut Vec<u8>);
    /// Recover an instance of Self from its binary representation. The `&mut Vec<u8>` argument may
    /// be taken with `mem::replace` if it is needed.
    fn from_bytes(&mut Vec<u8>) -> Self;
}

// NOTE : this should be unsafe, because these methods are.
// NOTE : figure this out later. don't use for serious things.
impl<T: Abomonation+Clone> Serialize for T {
    fn into_bytes(&mut self, bytes: &mut Vec<u8>) {
        unsafe { encode(self, bytes); }
    }
    fn from_bytes(bytes: &mut Vec<u8>) -> Self {
        (* unsafe { decode::<T>(bytes) }.unwrap().0).clone()
    }
}

/// Pushing elements of type `T`.
pub trait Push<T> {
    /// Pushes `element` and provides the opportunity to take ownership.
    ///
    /// The value of `element` after the call may be changed. A change does not imply anything other
    /// than that the implementor took resources associated with `element` and is returning other
    /// resources.
    fn push(&mut self, element: &mut Option<T>);
    /// Pushes `element` and drops any resulting resources.
    #[inline(always)]
    fn send(&mut self, element: T) { self.push(&mut Some(element)); }
    /// Pushes `None`, conventionally signalling a flush.
    #[inline(always)]
    fn done(&mut self) { self.push(&mut None); }
}

impl<T, P: ?Sized + Push<T>> Push<T> for Box<P> {
    #[inline(always)]
    fn push(&mut self, element: &mut Option<T>) { (**self).push(element) }
}

/// Pulling elements of type `T`.
pub trait Pull<T> {
    /// Pulls an element and provides the opportunity to take ownership.
    ///
    /// The receiver may mutate the result, in particular take ownership of the data by replacing
    /// it with other data or even `None`.
    /// If `pull` returns `None` this conventially signals that no more data is available
    /// at the moment.
    fn pull(&mut self) -> &mut Option<T>;
    /// Takes an `Option<T>` and leaves `None` behind.
    #[inline(always)]
    fn recv(&mut self) -> Option<T> { self.pull().take() }
}

impl<T, P: ?Sized + Pull<T>> Pull<T> for Box<P> {
    #[inline(always)]
    fn pull(&mut self) -> &mut Option<T> { (**self).pull() }
}
<|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 https://mozilla.org/MPL/2.0/. */

use glutin::os::ContextTraitExt;
use glutin::{NotCurrent, PossiblyCurrent, WindowedContext};
use servo_media::player::context::GlContext as RawContext;
use std::os::raw;

pub enum GlContext {
    Current(WindowedContext<PossiblyCurrent>),
    NotCurrent(WindowedContext<NotCurrent>),
    // Used a temporary value as we switch from Current to NotCurrent.
    None,
}

impl GlContext {
    pub fn window(&self) -> &glutin::Window {
        match self {
            GlContext::Current(c) => c.window(),
            GlContext::NotCurrent(c) => c.window(),
            GlContext::None => unreachable!(),
        }
    }
    pub fn resize(&mut self, size: glutin::dpi::PhysicalSize) {
        if let GlContext::NotCurrent(_) = self {
            self.make_current();
        }
        match self {
            GlContext::Current(c) => c.resize(size),
            _ => unreachable!(),
        }
    }
    pub fn make_current(&mut self) {
        *self = match std::mem::replace(self, GlContext::None) {
            GlContext::Current(c) => {
                warn!("Making an already current context current");
                GlContext::Current(c)
            },
            GlContext::NotCurrent(c) => {
                let c = unsafe {
                    c.make_current().expect("Couldn't make window current")
                };
                GlContext::Current(c)
            }
            GlContext::None => unreachable!(),
        }
    }
    pub fn make_not_current(&mut self) {
        *self = match std::mem::replace(self, GlContext::None) {
            GlContext::Current(c) => {
                let c = unsafe {
                    c.make_not_current().expect("Couldn't make window not current")
                };
                GlContext::NotCurrent(c)
            },
            GlContext::NotCurrent(c) => {
                warn!("Making an already not current context not current");
                GlContext::NotCurrent(c)
            }
            GlContext::None => unreachable!(),
        }
    }
    pub fn swap_buffers(&self) {
        match self {
            GlContext::Current(c) => {
                if let Err(err) = c.swap_buffers() {
                    warn!("Failed to swap window buffers ({}).", err);
                }
            },
            GlContext::NotCurrent(_) => {
                error!("Context is not current. Forgot to call prepare_for_composite?");
            },
            GlContext::None => unreachable!(),
        };
    }
    pub fn raw_context(&self) -> RawContext {
        match self {
            GlContext::Current(c) => {
                #[cfg(any(
                    target_os = "linux",
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd",
                ))]
                {
                    use glutin::os::unix::RawHandle;

                    let raw_handle = unsafe { c.raw_handle() };
                    return match raw_handle {
                        RawHandle::Egl(handle) => RawContext::Egl(handle as usize),
                        RawHandle::Glx(handle) => RawContext::Glx(handle as usize),
                    };
                }

                #[cfg(target_os = "windows")]
                {
                    use glutin::os::windows::RawHandle;

                    let raw_handle = unsafe { c.raw_handle() };
                    return match raw_handle {
                        RawHandle::Egl(handle) => RawContext::Egl(handle as usize),
                        // @TODO(victor): RawContext::Wgl in servo-media
                        RawHandle::Wgl(_) => unimplemented!(),
                    }
                }

                #[cfg(target_os = "android")]
                {
                    let raw_handle = unsafe { c.raw_handle() };
                    return RawContext::Egl(raw_handle as usize);
                }

                #[cfg(target_os = "macos")]
                return unimplemeneted!(); // @TODO(victor): RawContext::Cocoa in servo-media

                #[cfg(not(any(
                    target_os = "linux",
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd",
                    target_os = "windows",
                    target_os = "android",
                    target_os = "macos",
                )))]
                unimplemented!()
            }
            GlContext::NotCurrent(_) => {
                error!("Context is not current.");
                RawContext::Unknown
            }
            GlContext::None => unreachable!(),
        }
    }

    pub fn egl_display(&self) -> Option<*const raw::c_void> {
        match self {
            GlContext::Current(c) => unsafe { c.get_egl_display() },
            GlContext::NotCurrent(_) => {
                error!("Context is not current.");
                None
            },
            GlContext::None => unreachable!(),
        }
    }

    pub fn get_api(&self) -> glutin::Api {
        match self {
            GlContext::Current(c) => c.get_api(),
            GlContext::NotCurrent(c) => c.get_api(),
            GlContext::None => unreachable!(),
        }
    }
}
<commit_msg>Fix OSX build<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 https://mozilla.org/MPL/2.0/. */

use glutin::os::ContextTraitExt;
use glutin::{NotCurrent, PossiblyCurrent, WindowedContext};
use servo_media::player::context::GlContext as RawContext;
use std::os::raw;

pub enum GlContext {
    Current(WindowedContext<PossiblyCurrent>),
    NotCurrent(WindowedContext<NotCurrent>),
    // Used a temporary value as we switch from Current to NotCurrent.
    None,
}

impl GlContext {
    pub fn window(&self) -> &glutin::Window {
        match self {
            GlContext::Current(c) => c.window(),
            GlContext::NotCurrent(c) => c.window(),
            GlContext::None => unreachable!(),
        }
    }
    pub fn resize(&mut self, size: glutin::dpi::PhysicalSize) {
        if let GlContext::NotCurrent(_) = self {
            self.make_current();
        }
        match self {
            GlContext::Current(c) => c.resize(size),
            _ => unreachable!(),
        }
    }
    pub fn make_current(&mut self) {
        *self = match std::mem::replace(self, GlContext::None) {
            GlContext::Current(c) => {
                warn!("Making an already current context current");
                GlContext::Current(c)
            },
            GlContext::NotCurrent(c) => {
                let c = unsafe {
                    c.make_current().expect("Couldn't make window current")
                };
                GlContext::Current(c)
            }
            GlContext::None => unreachable!(),
        }
    }
    pub fn make_not_current(&mut self) {
        *self = match std::mem::replace(self, GlContext::None) {
            GlContext::Current(c) => {
                let c = unsafe {
                    c.make_not_current().expect("Couldn't make window not current")
                };
                GlContext::NotCurrent(c)
            },
            GlContext::NotCurrent(c) => {
                warn!("Making an already not current context not current");
                GlContext::NotCurrent(c)
            }
            GlContext::None => unreachable!(),
        }
    }
    pub fn swap_buffers(&self) {
        match self {
            GlContext::Current(c) => {
                if let Err(err) = c.swap_buffers() {
                    warn!("Failed to swap window buffers ({}).", err);
                }
            },
            GlContext::NotCurrent(_) => {
                error!("Context is not current. Forgot to call prepare_for_composite?");
            },
            GlContext::None => unreachable!(),
        };
    }
    pub fn raw_context(&self) -> RawContext {
        match self {
            GlContext::Current(c) => {
                #[cfg(any(
                    target_os = "linux",
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd",
                ))]
                {
                    use glutin::os::unix::RawHandle;

                    let raw_handle = unsafe { c.raw_handle() };
                    return match raw_handle {
                        RawHandle::Egl(handle) => RawContext::Egl(handle as usize),
                        RawHandle::Glx(handle) => RawContext::Glx(handle as usize),
                    };
                }

                #[cfg(target_os = "windows")]
                {
                    use glutin::os::windows::RawHandle;

                    let raw_handle = unsafe { c.raw_handle() };
                    return match raw_handle {
                        RawHandle::Egl(handle) => RawContext::Egl(handle as usize),
                        // @TODO(victor): RawContext::Wgl in servo-media
                        RawHandle::Wgl(_) => unimplemented!(),
                    }
                }

                #[cfg(target_os = "android")]
                {
                    let raw_handle = unsafe { c.raw_handle() };
                    return RawContext::Egl(raw_handle as usize);
                }

                #[cfg(target_os = "macos")]
                return unimplemented!(); // @TODO(victor): RawContext::Cocoa in servo-media

                #[cfg(not(any(
                    target_os = "linux",
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd",
                    target_os = "windows",
                    target_os = "android",
                    target_os = "macos",
                )))]
                unimplemented!()
            }
            GlContext::NotCurrent(_) => {
                error!("Context is not current.");
                RawContext::Unknown
            }
            GlContext::None => unreachable!(),
        }
    }

    pub fn egl_display(&self) -> Option<*const raw::c_void> {
        match self {
            GlContext::Current(c) => unsafe { c.get_egl_display() },
            GlContext::NotCurrent(_) => {
                error!("Context is not current.");
                None
            },
            GlContext::None => unreachable!(),
        }
    }

    pub fn get_api(&self) -> glutin::Api {
        match self {
            GlContext::Current(c) => c.get_api(),
            GlContext::NotCurrent(c) => c.get_api(),
            GlContext::None => unreachable!(),
        }
    }
}
<|endoftext|>


<commit_before>extern crate askama_shared as shared;
#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;

mod generator;
mod input;
mod parser;

use input::{Print, Source, TemplateInput};
use parser::{Expr, Macro, Node};
use proc_macro::TokenStream;
use shared::{read_config_file, Config};

use parser::parse;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let file = read_config_file();
    let config = Config::new(&file);
    let input = TemplateInput::new(ast, &config);
    let source: String = match input.source {
        Source::Source(ref s) => s.clone(),
        Source::Path(_) => get_template_source(&input.path),
    };

    let mut sources = HashMap::new();
    find_used_templates(&input, &mut sources, source);

    let mut parsed = HashMap::new();
    for (path, src) in &sources {
        parsed.insert(path, parse(src, input.syntax));
    }

    let mut contexts = HashMap::new();
    for (path, nodes) in &parsed {
        contexts.insert(*path, Context::new(&input.config, path, nodes));
    }

    let ctx = &contexts[&input.path];
    let heritage = if !ctx.blocks.is_empty() {
        Some(Heritage::new(ctx, &contexts))
    } else {
        None
    };

    if input.print == Print::Ast || input.print == Print::All {
        println!("{:?}", parsed[&input.path]);
    }

    let code = generator::generate(&input, &contexts, &heritage);
    if input.print == Print::Code || input.print == Print::All {
        println!("{}", code);
    }
    code
}

fn find_used_templates(input: &TemplateInput, map: &mut HashMap<PathBuf, String>, source: String) {
    let mut check = vec![(input.path.clone(), source)];
    while let Some((path, source)) = check.pop() {
        for n in parse(&source, input.syntax) {
            match n {
                Node::Extends(Expr::StrLit(extends)) => {
                    let extends = input.config.find_template(extends, Some(&path));
                    let source = get_template_source(&extends);
                    check.push((extends, source));
                }
                Node::Import(_, import, _) => {
                    let import = input.config.find_template(import, Some(&path));
                    let source = get_template_source(&import);
                    check.push((import, source));
                }
                _ => {}
            }
        }
        map.insert(path, source);
    }
}

pub(crate) struct Heritage<'a> {
    root: &'a Context<'a>,
    blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks
                    .entry(name)
                    .or_insert_with(|| vec![])
                    .push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub(crate) struct Context<'a> {
    nodes: &'a [Node<'a>],
    extends: Option<PathBuf>,
    blocks: HashMap<&'a str, &'a Node<'a>>,
    macros: HashMap<&'a str, &'a Macro<'a>>,
    imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();

        for n in nodes {
            match n {
                Node::Extends(Expr::StrLit(extends_path)) => match extends {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => {
                        extends = Some(config.find_template(extends_path, Some(path)));
                    }
                },
                def @ Node::BlockDef(_, _, _, _) => {
                    blocks.push(def);
                }
                Node::Macro(name, m) => {
                    macros.insert(*name, m);
                }
                Node::Import(_, import_path, scope) => {
                    let path = config.find_template(import_path, Some(path));
                    imports.insert(*scope, path);
                }
                _ => {}
            }
        }

        let mut check_nested = 0;
        let mut nested_blocks = Vec::new();
        while check_nested < blocks.len() {
            if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] {
                for n in nodes {
                    if let def @ Node::BlockDef(_, _, _, _) = n {
                        nested_blocks.push(def);
                    }
                }
            } else {
                panic!("non block found in list of blocks");
            }
            blocks.append(&mut nested_blocks);
            check_nested += 1;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            }).collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}

fn get_template_source(tpl_path: &Path) -> String {
    match fs::read_to_string(tpl_path) {
        Err(_) => panic!(
            "unable to open template file '{}'",
            tpl_path.to_str().unwrap()
        ),
        Ok(mut source) => {
            if source.ends_with('\n') {
                let _ = source.pop();
            }
            source
        }
    }
}

#[cfg(test)]
mod tests {
    use super::get_template_source;
    use Config;

    #[test]
    fn get_source() {
        let path = Config::new("").find_template("b.html", None);
        assert_eq!(get_template_source(&path), "bar");
    }
}
<commit_msg>Improve variable name to better reflect its type<commit_after>extern crate askama_shared as shared;
#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;

mod generator;
mod input;
mod parser;

use input::{Print, Source, TemplateInput};
use parser::{Expr, Macro, Node};
use proc_macro::TokenStream;
use shared::{read_config_file, Config};

use parser::parse;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let config_toml = read_config_file();
    let config = Config::new(&config_toml);
    let input = TemplateInput::new(ast, &config);
    let source: String = match input.source {
        Source::Source(ref s) => s.clone(),
        Source::Path(_) => get_template_source(&input.path),
    };

    let mut sources = HashMap::new();
    find_used_templates(&input, &mut sources, source);

    let mut parsed = HashMap::new();
    for (path, src) in &sources {
        parsed.insert(path, parse(src, input.syntax));
    }

    let mut contexts = HashMap::new();
    for (path, nodes) in &parsed {
        contexts.insert(*path, Context::new(&input.config, path, nodes));
    }

    let ctx = &contexts[&input.path];
    let heritage = if !ctx.blocks.is_empty() {
        Some(Heritage::new(ctx, &contexts))
    } else {
        None
    };

    if input.print == Print::Ast || input.print == Print::All {
        println!("{:?}", parsed[&input.path]);
    }

    let code = generator::generate(&input, &contexts, &heritage);
    if input.print == Print::Code || input.print == Print::All {
        println!("{}", code);
    }
    code
}

fn find_used_templates(input: &TemplateInput, map: &mut HashMap<PathBuf, String>, source: String) {
    let mut check = vec![(input.path.clone(), source)];
    while let Some((path, source)) = check.pop() {
        for n in parse(&source, input.syntax) {
            match n {
                Node::Extends(Expr::StrLit(extends)) => {
                    let extends = input.config.find_template(extends, Some(&path));
                    let source = get_template_source(&extends);
                    check.push((extends, source));
                }
                Node::Import(_, import, _) => {
                    let import = input.config.find_template(import, Some(&path));
                    let source = get_template_source(&import);
                    check.push((import, source));
                }
                _ => {}
            }
        }
        map.insert(path, source);
    }
}

pub(crate) struct Heritage<'a> {
    root: &'a Context<'a>,
    blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks
                    .entry(name)
                    .or_insert_with(|| vec![])
                    .push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub(crate) struct Context<'a> {
    nodes: &'a [Node<'a>],
    extends: Option<PathBuf>,
    blocks: HashMap<&'a str, &'a Node<'a>>,
    macros: HashMap<&'a str, &'a Macro<'a>>,
    imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();

        for n in nodes {
            match n {
                Node::Extends(Expr::StrLit(extends_path)) => match extends {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => {
                        extends = Some(config.find_template(extends_path, Some(path)));
                    }
                },
                def @ Node::BlockDef(_, _, _, _) => {
                    blocks.push(def);
                }
                Node::Macro(name, m) => {
                    macros.insert(*name, m);
                }
                Node::Import(_, import_path, scope) => {
                    let path = config.find_template(import_path, Some(path));
                    imports.insert(*scope, path);
                }
                _ => {}
            }
        }

        let mut check_nested = 0;
        let mut nested_blocks = Vec::new();
        while check_nested < blocks.len() {
            if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] {
                for n in nodes {
                    if let def @ Node::BlockDef(_, _, _, _) = n {
                        nested_blocks.push(def);
                    }
                }
            } else {
                panic!("non block found in list of blocks");
            }
            blocks.append(&mut nested_blocks);
            check_nested += 1;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            }).collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}

fn get_template_source(tpl_path: &Path) -> String {
    match fs::read_to_string(tpl_path) {
        Err(_) => panic!(
            "unable to open template file '{}'",
            tpl_path.to_str().unwrap()
        ),
        Ok(mut source) => {
            if source.ends_with('\n') {
                let _ = source.pop();
            }
            source
        }
    }
}

#[cfg(test)]
mod tests {
    use super::get_template_source;
    use Config;

    #[test]
    fn get_source() {
        let path = Config::new("").find_template("b.html", None);
        assert_eq!(get_template_source(&path), "bar");
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use self::rand::{thread_rng, Rng};

use super::srs;
use super::srs::Direction;
use super::tetromino::{
    Point,
    Rotation,
    Tetromino,
    TetrominoType,
};

pub const WIDTH: usize = 10;
pub const HEIGHT: usize = 22;

const SPAWN: Point = Point { x: 3, y: 0 };

const LEFT: Point = Point { x: -1, y: 0 };
const RIGHT: Point = Point { x: 1, y: 0 };
const UP: Point = Point { x: 0, y: -1 };
const DOWN: Point = Point { x: 0, y: 1 };

const SOFT_DROP: usize = 1;
const HARD_DROP: usize = 2;

pub type Field = [[Option<TetrominoType>; WIDTH]; HEIGHT];

/// A struct representing a 10x22 Tetris board
pub struct Board {
    field: Field,
    curr: Tetromino,
    ghost: Tetromino,
    next: Vec<TetrominoType>,
    is_topped_out: bool,
    score: usize,
    level: usize,
    cleared: usize,
}

impl Board {

    /// Initializes a new Board struct
    pub fn new() -> Self {

        // Create a sequence of pieces and shuffle them
        let mut next = vec![
            TetrominoType::I,
            TetrominoType::J,
            TetrominoType::L,
            TetrominoType::O,
            TetrominoType::S,
            TetrominoType::T,
            TetrominoType::Z,
        ];

        let mut rng = thread_rng();
        rng.shuffle(&mut next);

        let tetromino = Tetromino::new(SPAWN, next.pop().unwrap(), Rotation::Spawn);

        let mut board = Board {
            field: [[None; WIDTH]; HEIGHT],
            curr: tetromino,
            ghost: Tetromino::new_ghost(&tetromino),
            next: next,
            is_topped_out: false,
            score: 0,
            cleared: 0,
            level: 0,
        };

        board.add_current();
        board.drop_ghost(false);
        board
    }

    /// Applies gravity and clears any lines
    pub fn tick(&mut self) {
        self.apply_gravity();
    }

    /// Applies gravity to the field
    fn apply_gravity(&mut self) {
        let offset = DOWN;

        if self.is_moveable(offset) {
            self.do_move(offset);
            self.score += SOFT_DROP;
        }

        else {
            self.clear_lines();
            self.spawn();
        }
    }

    /// Clears any lines from the field completed by the current Tetromino
    fn clear_lines(&mut self) {
        let mut cleared = vec![];

        for &mino in self.curr.minos().iter() {
            let row = (self.curr.origin().y + mino.y) as usize;

            if self.is_line(row) {
                for col in 0..WIDTH {
                    self.field[row][col] = None;
                }

                cleared.push(row);
            }
        }

        // Drop the rows if any line clears were made
        if cleared.first().is_some() {
            self.drop_rows(*cleared.first().unwrap(), cleared.len());

            // Update the player's status
            self.cleared += cleared.len();

            if self.cleared % 10 == 0 {
                self.level += 1;
            }

            self.score_clear(cleared.len());
        }
    }

    /// Determines if a specific row in the field is a complete line
    fn is_line(&self, row: usize) -> bool {
        for col in 0..WIDTH {
            match self.field[row][col] {
                Some(TetrominoType::Ghost) | None => return false,
                _ => { },
            }
        }

        true
    }

    /// Drops all rows above the given start row by drop
    fn drop_rows(&mut self, start: usize, drop: usize) {

        // We must use rev() because Rust doesn't support backwards iteration
        for row in (0..start).rev() {
            for col in 0..WIDTH {
                if self.field[row][col].is_some() {
                    self.field[row + drop][col] = self.field[row][col];
                    self.field[row][col] = None;
                }
            }
        }
    }

    /// Increases the score based on the number of lines cleared
    fn score_clear(&mut self, lines: usize) {

        // The scoring formula used is identical to the NES version
        self.score += match lines {
            1 =>   40 * (self.level + 1),
            2 =>  100 * (self.level + 1),
            3 =>  300 * (self.level + 1),
            4 => 1200 * (self.level + 1),

            // 4+ lines cleared is impossible in the current system.
            _ => 0,
        }
    }

    /// Moves the current Tetromino to the left
    pub fn left(&mut self) {
        self.move_tetromino(LEFT);
    }

    /// Moves the current Tetromino to the right
    pub fn right(&mut self) {
        self.move_tetromino(RIGHT);
    }

    /// Moves the current Tetromino up
    pub fn up(&mut self) {
        self.move_tetromino(UP);
    }

    /// Moves the current Tetromino down
    pub fn down(&mut self) {
        self.move_tetromino(DOWN);
    }

    /// Continually drops the current Tetromino until it locks
    pub fn drop_tetromino(&mut self) {
        while self.is_moveable(DOWN) {
            self.do_move(DOWN);
            self.score += HARD_DROP;
        }

        self.clear_lines();
        self.spawn();
    }

    /// Moves the current Tetromino by an (x, y) offset
    fn move_tetromino(&mut self, offset: Point) {
        if self.is_moveable(offset) {
            self.do_move(offset);
            self.drop_ghost(true
                );
        }
    }

    /// Determines if an offset to the current Tetromino is possible or not
    fn is_moveable(&self, offset: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino + offset;

            // Determine if the field's boundaries are respected
            if pos.x < 0 || pos.y < 0 || (pos.x as usize) >= WIDTH || (pos.y as usize) >= HEIGHT {
                return false;
            }

             // Determine if the current Tetromino overlaps only itself
            if self.field[pos.y as usize][pos.x as usize].is_some() && !self.overlaps_active(pos) {
                if let Some(TetrominoType::Ghost) = self.field[pos.y as usize][pos.x as usize] {
                    continue;
                }

                else {
                    return false;
                }
            }
        }

        true
    }

    /// Determines if an offset to the current Tetromino would cause it to
    /// only overlap itself or not
    fn overlaps_active(&self, new: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            if new.x == pos.x && new.y == pos.y {
                return true;
            }
        }

        false
    }

    /// Performs the actual movement
    fn do_move(&mut self, offset: Point) {
        let mut minos = self.curr.minos().clone();

        // Moving down or right requires us to handle the blocks in
        // reverse order to prevent blocks from being erased
        match offset {
            RIGHT | DOWN => minos.reverse(),
            _ => { },
        }

        // Perform the move
        for &mino in minos.iter() {
            let org = self.curr.origin() + mino;
            let pos = org + offset;

            self.field[pos.y as usize][pos.x as usize] = self.field[org.y as usize][org.x as usize];
            self.field[org.y as usize][org.x as usize] = None;
        }

        let origin = self.curr.origin() + offset;
        self.curr.set_origin(origin);
    }

    /// Drops the ghost Tetromino beneath the current Tetromino
    fn drop_ghost(&mut self, erase: bool) {

        // Remove the existing ghost Minos from the board if necessary
        if erase {
            for &mino in self.ghost.minos().iter() {
                let pos = self.ghost.origin() + mino;
                self.field[pos.y as usize][pos.x as usize] = None;
            }
        }

        self.ghost = Tetromino::new_ghost(&self.curr);

        // Determine the position that the ghost should drop down to
        while self.is_ghost_moveable() {
            let origin = self.ghost.origin() + DOWN;
            self.ghost.set_origin(origin);
        }

        // Perform the drop
        for &mino in self.ghost.minos().iter() {
            let pos = self.ghost.origin() + mino;

            if !self.overlaps_active(pos) {
                self.field[pos.y as usize][pos.x as usize] = Some(TetrominoType::Ghost);
            }
        }
    }

    /// Determines if the ghost Tetromino can be moved down
    fn is_ghost_moveable(&self) -> bool {
        for &mino in self.ghost.minos().iter() {
            let pos = self.ghost.origin() + mino + DOWN;

            if pos.y as usize >= HEIGHT {
                return false;
            }

            match self.field[pos.y as usize][pos.x as usize] {
                Some(TetrominoType::Ghost) | None => { }
                Some(..) => return self.overlaps_active(pos),
            }
        }

        true
    }

    /// Rotates the current Tetromino in a specified direction
    pub fn rotate(&mut self, dir: Direction) {
        match srs::rotate(&self.field, &self.curr, dir) {
            Some((field, rotated)) => {
                self.field = field;
                self.curr = rotated;
                self.drop_ghost(true);
            },

            None => { },
        }
    }

    /// Peeks at the next Tetromino
    pub fn peek_next(&self) -> Tetromino {
        Tetromino::new(SPAWN, *self.next.last().unwrap(), Rotation::Spawn)
    }

    /// Spawns the next Tetromino in the sequence
    fn spawn(&mut self) {
        self.curr = Tetromino::new(SPAWN, self.next.pop().unwrap(), Rotation::Spawn);
        self.drop_ghost(false);
        self.add_current();

        // All of the pieces have been picked, so reshuffle them
        if self.next.is_empty() {
            self.next = vec![
                TetrominoType::I,
                TetrominoType::J,
                TetrominoType::L,
                TetrominoType::O,
                TetrominoType::S,
                TetrominoType::T,
                TetrominoType::Z,
            ];

            let mut rng = thread_rng();
            rng.shuffle(&mut self.next);
        }
    }

    /// Adds the current Tetromino to the field
    fn add_current(&mut self) {

        // Add the new blocks to the board
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            // The field has been topped out and the game is over
            if self.field[pos.y as usize][pos.x as usize].is_some() {
                self.is_topped_out = true;
                break;
            }

            self.field[pos.y as usize][pos.x as usize] = Some(self.curr.tetromino_type());
        }
    }

    // GETTERS / SETTERS

    pub fn field(&self) -> Field {
        self.field
    }

    pub fn is_topped_out(&self) -> bool {
        self.is_topped_out
    }

    pub fn score(&self) -> usize {
        self.score
    }

    pub fn level(&self) -> usize {
        self.level
    }

    pub fn cleared(&self) -> usize {
        self.cleared
    }
}<commit_msg>Fix issue where current Tetromino would be erased if overlapping its ghost<commit_after>extern crate rand;

use self::rand::{thread_rng, Rng};

use super::srs;
use super::srs::Direction;
use super::tetromino::{
    Point,
    Rotation,
    Tetromino,
    TetrominoType,
};

pub const WIDTH: usize = 10;
pub const HEIGHT: usize = 22;

const SPAWN: Point = Point { x: 3, y: 0 };

const LEFT: Point = Point { x: -1, y: 0 };
const RIGHT: Point = Point { x: 1, y: 0 };
const UP: Point = Point { x: 0, y: -1 };
const DOWN: Point = Point { x: 0, y: 1 };

const SOFT_DROP: usize = 1;
const HARD_DROP: usize = 2;

pub type Field = [[Option<TetrominoType>; WIDTH]; HEIGHT];

/// A struct representing a 10x22 Tetris board
pub struct Board {
    field: Field,
    curr: Tetromino,
    ghost: Tetromino,
    next: Vec<TetrominoType>,
    is_topped_out: bool,
    score: usize,
    level: usize,
    cleared: usize,
}

impl Board {

    /// Initializes a new Board struct
    pub fn new() -> Self {

        // Create a sequence of pieces and shuffle them
        let mut next = vec![
            TetrominoType::I,
            TetrominoType::J,
            TetrominoType::L,
            TetrominoType::O,
            TetrominoType::S,
            TetrominoType::T,
            TetrominoType::Z,
        ];

        let mut rng = thread_rng();
        rng.shuffle(&mut next);

        let tetromino = Tetromino::new(SPAWN, next.pop().unwrap(), Rotation::Spawn);

        let mut board = Board {
            field: [[None; WIDTH]; HEIGHT],
            curr: tetromino,
            ghost: Tetromino::new_ghost(&tetromino),
            next: next,
            is_topped_out: false,
            score: 0,
            cleared: 0,
            level: 0,
        };

        board.add_current();
        board.drop_ghost(false);
        board
    }

    /// Applies gravity and clears any lines
    pub fn tick(&mut self) {
        self.apply_gravity();
    }

    /// Applies gravity to the field
    fn apply_gravity(&mut self) {
        let offset = DOWN;

        if self.is_moveable(offset) {
            self.do_move(offset);
            self.score += SOFT_DROP;
        }

        else {
            self.clear_lines();
            self.spawn();
        }
    }

    /// Clears any lines from the field completed by the current Tetromino
    fn clear_lines(&mut self) {
        let mut cleared = vec![];

        for &mino in self.curr.minos().iter() {
            let row = (self.curr.origin().y + mino.y) as usize;

            if self.is_line(row) {
                for col in 0..WIDTH {
                    self.field[row][col] = None;
                }

                cleared.push(row);
            }
        }

        // Drop the rows if any line clears were made
        if cleared.first().is_some() {
            self.drop_rows(*cleared.first().unwrap(), cleared.len());

            // Update the player's status
            self.cleared += cleared.len();

            if self.cleared % 10 == 0 {
                self.level += 1;
            }

            self.score_clear(cleared.len());
        }
    }

    /// Determines if a specific row in the field is a complete line
    fn is_line(&self, row: usize) -> bool {
        for col in 0..WIDTH {
            match self.field[row][col] {
                Some(TetrominoType::Ghost) | None => return false,
                _ => { },
            }
        }

        true
    }

    /// Drops all rows above the given start row by drop
    fn drop_rows(&mut self, start: usize, drop: usize) {

        // We must use rev() because Rust doesn't support backwards iteration
        for row in (0..start).rev() {
            for col in 0..WIDTH {
                if self.field[row][col].is_some() {
                    self.field[row + drop][col] = self.field[row][col];
                    self.field[row][col] = None;
                }
            }
        }
    }

    /// Increases the score based on the number of lines cleared
    fn score_clear(&mut self, lines: usize) {

        // The scoring formula used is identical to the NES version
        self.score += match lines {
            1 =>   40 * (self.level + 1),
            2 =>  100 * (self.level + 1),
            3 =>  300 * (self.level + 1),
            4 => 1200 * (self.level + 1),

            // 4+ lines cleared is impossible in the current system.
            _ => 0,
        }
    }

    /// Moves the current Tetromino to the left
    pub fn left(&mut self) {
        self.move_tetromino(LEFT);
    }

    /// Moves the current Tetromino to the right
    pub fn right(&mut self) {
        self.move_tetromino(RIGHT);
    }

    /// Moves the current Tetromino up
    pub fn up(&mut self) {
        self.move_tetromino(UP);
    }

    /// Moves the current Tetromino down
    pub fn down(&mut self) {
        self.move_tetromino(DOWN);
    }

    /// Continually drops the current Tetromino until it locks
    pub fn drop_tetromino(&mut self) {
        while self.is_moveable(DOWN) {
            self.do_move(DOWN);
            self.score += HARD_DROP;
        }

        self.clear_lines();
        self.spawn();
    }

    /// Moves the current Tetromino by an (x, y) offset
    fn move_tetromino(&mut self, offset: Point) {
        if self.is_moveable(offset) {
            self.do_move(offset);
            self.drop_ghost(true
                );
        }
    }

    /// Determines if an offset to the current Tetromino is possible or not
    fn is_moveable(&self, offset: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino + offset;

            // Determine if the field's boundaries are respected
            if pos.x < 0 || pos.y < 0 || (pos.x as usize) >= WIDTH || (pos.y as usize) >= HEIGHT {
                return false;
            }

             // Determine if the current Tetromino overlaps only itself
            if self.field[pos.y as usize][pos.x as usize].is_some() && !self.overlaps_active(pos) {
                if let Some(TetrominoType::Ghost) = self.field[pos.y as usize][pos.x as usize] {
                    continue;
                }

                else {
                    return false;
                }
            }
        }

        true
    }

    /// Determines if an offset to the current Tetromino would cause it to
    /// only overlap itself or not
    fn overlaps_active(&self, new: Point) -> bool {
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            if new.x == pos.x && new.y == pos.y {
                return true;
            }
        }

        false
    }

    /// Performs the actual movement
    fn do_move(&mut self, offset: Point) {
        let mut minos = self.curr.minos().clone();

        // Moving down or right requires us to handle the blocks in
        // reverse order to prevent blocks from being erased
        match offset {
            RIGHT | DOWN => minos.reverse(),
            _ => { },
        }

        // Perform the move
        for &mino in minos.iter() {
            let org = self.curr.origin() + mino;
            let pos = org + offset;

            self.field[pos.y as usize][pos.x as usize] = self.field[org.y as usize][org.x as usize];
            self.field[org.y as usize][org.x as usize] = None;
        }

        let origin = self.curr.origin() + offset;
        self.curr.set_origin(origin);
    }

    /// Drops the ghost Tetromino beneath the current Tetromino
    fn drop_ghost(&mut self, erase: bool) {

        // Remove the existing ghost from the board
        if erase {
            for &mino in self.ghost.minos().iter() {
                let pos = self.ghost.origin() + mino;

                if let Some(TetrominoType::Ghost) = self.field[pos.y as usize][pos.x as usize] {
                    self.field[pos.y as usize][pos.x as usize] = None;
                }
            }
        }

        self.ghost = Tetromino::new_ghost(&self.curr);

        // Determine the position that the ghost should drop down to
        while self.is_ghost_moveable() {
            let origin = self.ghost.origin() + DOWN;
            self.ghost.set_origin(origin);
        }

        // Perform the ghost drop
        for &mino in self.ghost.minos().iter() {
            let pos = self.ghost.origin() + mino;

            if !self.overlaps_active(pos) {
                self.field[pos.y as usize][pos.x as usize] = Some(TetrominoType::Ghost);
            }
        }
    }

    /// Determines if the ghost Tetromino can be moved down
    fn is_ghost_moveable(&self) -> bool {
        for &mino in self.ghost.minos().iter() {
            let pos = self.ghost.origin() + mino + DOWN;

            // Check that the boundary conditions are respected. We do not need
            // to check for x-axis boundaries because a ghost is located directly
            // underneath the current Tetromino
            if pos.y as usize >= HEIGHT {
                return false;
            }

            // Determine if acceptable overlap exists. We consider other
            // ghosts and active Tetrominos to be acceptable for overlaps.
            match self.field[pos.y as usize][pos.x as usize] {
                Some(TetrominoType::Ghost) | None => { }
                Some(..) => {

                    if self.overlaps_active(pos) {
                        continue;
                    }

                    else {
                        return false;
                    }
                },
            }
        }

        true
    }

    /// Rotates the current Tetromino in a specified direction
    pub fn rotate(&mut self, dir: Direction) {
        match srs::rotate(&self.field, &self.curr, dir) {
            Some((field, rotated)) => {
                self.field = field;
                self.curr = rotated;
                self.drop_ghost(true);
            },

            None => { },
        }
    }

    /// Peeks at the next Tetromino
    pub fn peek_next(&self) -> Tetromino {
        Tetromino::new(SPAWN, *self.next.last().unwrap(), Rotation::Spawn)
    }

    /// Spawns the next Tetromino in the sequence
    fn spawn(&mut self) {
        self.curr = Tetromino::new(SPAWN, self.next.pop().unwrap(), Rotation::Spawn);
        self.drop_ghost(false);
        self.add_current();

        // All of the pieces have been picked, so reshuffle them
        if self.next.is_empty() {
            self.next = vec![
                TetrominoType::I,
                TetrominoType::J,
                TetrominoType::L,
                TetrominoType::O,
                TetrominoType::S,
                TetrominoType::T,
                TetrominoType::Z,
            ];

            let mut rng = thread_rng();
            rng.shuffle(&mut self.next);
        }
    }

    /// Adds the current Tetromino to the field
    fn add_current(&mut self) {

        // Add the new blocks to the board
        for &mino in self.curr.minos().iter() {
            let pos = self.curr.origin() + mino;

            // The field has been topped out and the game is over
            if self.field[pos.y as usize][pos.x as usize].is_some() {
                self.is_topped_out = true;
                break;
            }

            self.field[pos.y as usize][pos.x as usize] = Some(self.curr.tetromino_type());
        }
    }

    // GETTERS / SETTERS

    pub fn field(&self) -> Field {
        self.field
    }

    pub fn is_topped_out(&self) -> bool {
        self.is_topped_out
    }

    pub fn score(&self) -> usize {
        self.score
    }

    pub fn level(&self) -> usize {
        self.level
    }

    pub fn cleared(&self) -> usize {
        self.cleared
    }
}<|endoftext|>