<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 enums;
use parse;

/// 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,
            } => {
                let (s, _) = write_struct(doc, result_id, member_types);
                result.push_str(&s);
                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)
    }
}

/// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String, Option<usize>) {
    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(), None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
        }

        // 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(), None),        // TODO: shouldn't we return and let the caller ignore it instead?
        };

        // 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(), "")
    };

    let s = 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);
    (s,
     spirv_req_total_size
         .map(|sz| sz as usize)
         .or(current_rust_offset))
}

/// 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 => {
                panic!("Can't put booleans in structs")
            },
            &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) = write_struct(doc, result_id, member_types);
                let align = member_types
                    .iter()
                    .map(|&t| type_from_id(doc, t).2)
                    .max()
                    .unwrap_or(1);
                return (name, size, align);
            },
            _ => (),
        }
    }

    panic!("Type #{} not found", searched)
}
<commit_msg>Allow non_snake_case for auto-generated structs (#796)<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 enums;
use parse;

/// 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,
            } => {
                let (s, _) = write_struct(doc, result_id, member_types);
                result.push_str(&s);
                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)
    }
}

/// Analyzes a single struct, returns a string containing its Rust definition, plus its size.
fn write_struct(doc: &parse::Spirv, struct_id: u32, members: &[u32]) -> (String, Option<usize>) {
    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(), None); // TODO: is this correct? shouldn't it return a correct struct but with a flag or something?
        }

        // 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(), None),        // TODO: shouldn't we return and let the caller ignore it instead?
        };

        // 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(), "")
    };

    let s = format!("#[repr(C)]\n\
                     {derive_text}\n\
                     #[allow(non_snake_case)]\n\
                     pub 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);
    (s,
     spirv_req_total_size
         .map(|sz| sz as usize)
         .or(current_rust_offset))
}

/// 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 => {
                panic!("Can't put booleans in structs")
            },
            &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) = write_struct(doc, result_id, member_types);
                let align = member_types
                    .iter()
                    .map(|&t| type_from_id(doc, t).2)
                    .max()
                    .unwrap_or(1);
                return (name, size, align);
            },
            _ => (),
        }
    }

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


<commit_before>//! Type representing a Liquid object, payload of the `Value::Object` variant

pub mod map;
mod ser;

use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;

use kstring::KStringCow;

use crate::model::value::DisplayCow;
use crate::model::State;
use crate::model::{Value, ValueView};

pub use map::Object;
pub use ser::to_object;

/// Accessor for objects.
pub trait ObjectView: ValueView {
    /// Cast to ValueView
    fn as_value(&self) -> &dyn ValueView;

    /// Returns the number of elements.
    fn size(&self) -> i64;

    /// Keys available for lookup.
    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>;
    /// Keys available for lookup.
    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>;
    /// Returns an iterator .
    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>;

    /// Access a contained `BoxedValue`.
    fn contains_key(&self, index: &str) -> bool;
    /// Access a contained `Value`.
    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>;
}

impl ValueView for Object {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(self.clone())
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl ObjectView for Object {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = Object::keys(self).map(|s| s.as_ref().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = Object::values(self).map(|v| v.as_view());
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = Object::iter(self).map(|(k, v)| (k.as_str().into(), v.as_view()));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        Object::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        Object::get(self, index).map(|v| v.as_view())
    }
}

impl<'o, O: ObjectView + ?Sized> ObjectView for &'o O {
    fn as_value(&self) -> &dyn ValueView {
        <O as ObjectView>::as_value(self)
    }

    fn size(&self) -> i64 {
        <O as ObjectView>::size(self)
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        <O as ObjectView>::keys(self)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        <O as ObjectView>::values(self)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        <O as ObjectView>::iter(self)
    }

    fn contains_key(&self, index: &str) -> bool {
        <O as ObjectView>::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        <O as ObjectView>::get(self, index)
    }
}

/// Owned object index
pub trait ObjectIndex:
    fmt::Debug + fmt::Display + Ord + std::hash::Hash + Eq + std::borrow::Borrow<str>
{
    /// Borrow the index
    fn as_index(&self) -> &str;
}

impl ObjectIndex for String {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl ObjectIndex for kstring::KString {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ValueView for HashMap<K, V, S> {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(
            self.iter()
                .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
                .collect(),
        )
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for HashMap<K, V, S> {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = HashMap::keys(self).map(|s| s.as_index().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = HashMap::values(self).map(as_view);
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = HashMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        HashMap::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        HashMap::get(self, index).map(as_view)
    }
}

impl<K: ObjectIndex, V: ValueView> ValueView for BTreeMap<K, V> {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(
            self.iter()
                .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
                .collect(),
        )
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = BTreeMap::keys(self).map(|s| s.as_index().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = BTreeMap::values(self).map(as_view);
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = BTreeMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        BTreeMap::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        BTreeMap::get(self, index).map(as_view)
    }
}

fn as_view<T: ValueView>(value: &T) -> &dyn ValueView {
    value
}

#[derive(Debug)]
/// Helper for `ObjectView::source`
pub struct ObjectSource<'s, O: ObjectView> {
    s: &'s O,
}

impl<'s, O: ObjectView> ObjectSource<'s, O> {
    #[doc(hidden)]
    pub fn new(other: &'s O) -> Self {
        Self { s: other }
    }
}

impl<'s, O: ObjectView> fmt::Display for ObjectSource<'s, O> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{{")?;
        for (k, v) in self.s.iter() {
            write!(f, r#""{}": {}, "#, k, v.render())?;
        }
        write!(f, "}}")?;
        Ok(())
    }
}

#[derive(Debug)]
/// Helper for `ObjectView::render`
pub struct ObjectRender<'s, O: ObjectView> {
    s: &'s O,
}

impl<'s, O: ObjectView> ObjectRender<'s, O> {
    #[doc(hidden)]
    pub fn new(other: &'s O) -> Self {
        Self { s: other }
    }
}

impl<'s, O: ObjectView> fmt::Display for ObjectRender<'s, O> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (k, v) in self.s.iter() {
            write!(f, "{}{}", k, v.render())?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_object() {
        let obj = Object::new();
        println!("{}", obj.source());
        let object: &dyn ObjectView = &obj;
        println!("{}", object.source());
        let view: &dyn ValueView = object.as_value();
        println!("{}", view.source());
    }
}
<commit_msg>feat(model): Allow references as keys in objects<commit_after>//! Type representing a Liquid object, payload of the `Value::Object` variant

pub mod map;
mod ser;

use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;

use kstring::KStringCow;

use crate::model::value::DisplayCow;
use crate::model::State;
use crate::model::{Value, ValueView};

pub use map::Object;
pub use ser::to_object;

/// Accessor for objects.
pub trait ObjectView: ValueView {
    /// Cast to ValueView
    fn as_value(&self) -> &dyn ValueView;

    /// Returns the number of elements.
    fn size(&self) -> i64;

    /// Keys available for lookup.
    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k>;
    /// Keys available for lookup.
    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k>;
    /// Returns an iterator .
    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k>;

    /// Access a contained `BoxedValue`.
    fn contains_key(&self, index: &str) -> bool;
    /// Access a contained `Value`.
    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView>;
}

impl ValueView for Object {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(self.clone())
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl ObjectView for Object {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = Object::keys(self).map(|s| s.as_ref().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = Object::values(self).map(|v| v.as_view());
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = Object::iter(self).map(|(k, v)| (k.as_str().into(), v.as_view()));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        Object::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        Object::get(self, index).map(|v| v.as_view())
    }
}

impl<'o, O: ObjectView + ?Sized> ObjectView for &'o O {
    fn as_value(&self) -> &dyn ValueView {
        <O as ObjectView>::as_value(self)
    }

    fn size(&self) -> i64 {
        <O as ObjectView>::size(self)
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        <O as ObjectView>::keys(self)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        <O as ObjectView>::values(self)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        <O as ObjectView>::iter(self)
    }

    fn contains_key(&self, index: &str) -> bool {
        <O as ObjectView>::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        <O as ObjectView>::get(self, index)
    }
}

/// Owned object index
pub trait ObjectIndex:
    fmt::Debug + fmt::Display + Ord + std::hash::Hash + Eq + std::borrow::Borrow<str>
{
    /// Borrow the index
    fn as_index(&self) -> &str;
}

impl ObjectIndex for String {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl ObjectIndex for kstring::KString {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl<'s> ObjectIndex for kstring::KStringRef<'s> {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl<'s> ObjectIndex for kstring::KStringCow<'s> {
    fn as_index(&self) -> &str {
        self.as_str()
    }
}

impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ValueView for HashMap<K, V, S> {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(
            self.iter()
                .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
                .collect(),
        )
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for HashMap<K, V, S> {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = HashMap::keys(self).map(|s| s.as_index().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = HashMap::values(self).map(as_view);
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = HashMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        HashMap::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        HashMap::get(self, index).map(as_view)
    }
}

impl<K: ObjectIndex, V: ValueView> ValueView for BTreeMap<K, V> {
    fn as_debug(&self) -> &dyn fmt::Debug {
        self
    }

    fn render(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectRender { s: self }))
    }
    fn source(&self) -> DisplayCow<'_> {
        DisplayCow::Owned(Box::new(ObjectSource { s: self }))
    }
    fn type_name(&self) -> &'static str {
        "object"
    }
    fn query_state(&self, state: State) -> bool {
        match state {
            State::Truthy => true,
            State::DefaultValue | State::Empty | State::Blank => self.is_empty(),
        }
    }

    fn to_kstr(&self) -> KStringCow<'_> {
        let s = ObjectRender { s: self }.to_string();
        KStringCow::from_string(s)
    }
    fn to_value(&self) -> Value {
        Value::Object(
            self.iter()
                .map(|(k, v)| (kstring::KString::from_ref(k.as_index()), v.to_value()))
                .collect(),
        )
    }

    fn as_object(&self) -> Option<&dyn ObjectView> {
        Some(self)
    }
}

impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
    fn as_value(&self) -> &dyn ValueView {
        self
    }

    fn size(&self) -> i64 {
        self.len() as i64
    }

    fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
        let keys = BTreeMap::keys(self).map(|s| s.as_index().into());
        Box::new(keys)
    }

    fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
        let i = BTreeMap::values(self).map(as_view);
        Box::new(i)
    }

    fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
        let i = BTreeMap::iter(self).map(|(k, v)| (k.as_index().into(), as_view(v)));
        Box::new(i)
    }

    fn contains_key(&self, index: &str) -> bool {
        BTreeMap::contains_key(self, index)
    }

    fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
        BTreeMap::get(self, index).map(as_view)
    }
}

fn as_view<T: ValueView>(value: &T) -> &dyn ValueView {
    value
}

#[derive(Debug)]
/// Helper for `ObjectView::source`
pub struct ObjectSource<'s, O: ObjectView> {
    s: &'s O,
}

impl<'s, O: ObjectView> ObjectSource<'s, O> {
    #[doc(hidden)]
    pub fn new(other: &'s O) -> Self {
        Self { s: other }
    }
}

impl<'s, O: ObjectView> fmt::Display for ObjectSource<'s, O> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{{")?;
        for (k, v) in self.s.iter() {
            write!(f, r#""{}": {}, "#, k, v.render())?;
        }
        write!(f, "}}")?;
        Ok(())
    }
}

#[derive(Debug)]
/// Helper for `ObjectView::render`
pub struct ObjectRender<'s, O: ObjectView> {
    s: &'s O,
}

impl<'s, O: ObjectView> ObjectRender<'s, O> {
    #[doc(hidden)]
    pub fn new(other: &'s O) -> Self {
        Self { s: other }
    }
}

impl<'s, O: ObjectView> fmt::Display for ObjectRender<'s, O> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (k, v) in self.s.iter() {
            write!(f, "{}{}", k, v.render())?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_object() {
        let obj = Object::new();
        println!("{}", obj.source());
        let object: &dyn ObjectView = &obj;
        println!("{}", object.source());
        let view: &dyn ValueView = object.as_value();
        println!("{}", view.source());
    }
}
<|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    SourceFile, SyntaxKind, TextSize, T,
};
use test_utils::mark;

// Feature: Matching Brace
//
// If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair,
// moves cursor to the matching brace. It uses the actual parser to determine
// braces, so it won't confuse generics with comparisons.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Find matching brace**
// |===
pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
    const BRACES: &[SyntaxKind] =
        &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
    let (brace_token, brace_idx) = file
        .syntax()
        .token_at_offset(offset)
        .filter_map(|node| {
            let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
            Some((node, idx))
        })
        .next()?;
    let parent = brace_token.parent();
    if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
        mark::hit!(pipes_not_braces);
        return None;
    }
    let matching_kind = BRACES[brace_idx ^ 1];
    let matching_node = parent.children_with_tokens().find(|node| node.kind() == matching_kind)?;
    Some(matching_node.text_range().start())
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use super::*;

    #[test]
    fn test_matching_brace() {
        fn do_check(before: &str, after: &str) {
            let (pos, before) = extract_offset(before);
            let parse = SourceFile::parse(&before);
            let new_pos = match matching_brace(&parse.tree(), pos) {
                None => pos,
                Some(pos) => pos,
            };
            let actual = add_cursor(&before, new_pos);
            assert_eq_text!(after, &actual);
        }

        do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
        do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}");

        {
            mark::check!(pipes_not_braces);
            do_check(
                "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
                "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
            );
        }
    }
}
<commit_msg>Fix matchig brace for pipes<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    SourceFile, SyntaxKind, TextSize, T,
};
use test_utils::mark;

// Feature: Matching Brace
//
// If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair,
// moves cursor to the matching brace. It uses the actual parser to determine
// braces, so it won't confuse generics with comparisons.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Find matching brace**
// |===
pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
    const BRACES: &[SyntaxKind] =
        &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
    let (brace_token, brace_idx) = file
        .syntax()
        .token_at_offset(offset)
        .filter_map(|node| {
            let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
            Some((node, idx))
        })
        .next()?;
    let parent = brace_token.parent();
    if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
        mark::hit!(pipes_not_braces);
        return None;
    }
    let matching_kind = BRACES[brace_idx ^ 1];
    let matching_node = parent
        .children_with_tokens()
        .filter_map(|it| it.into_token())
        .find(|node| node.kind() == matching_kind && node != &brace_token)?;
    Some(matching_node.text_range().start())
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use super::*;

    #[test]
    fn test_matching_brace() {
        fn do_check(before: &str, after: &str) {
            let (pos, before) = extract_offset(before);
            let parse = SourceFile::parse(&before);
            let new_pos = match matching_brace(&parse.tree(), pos) {
                None => pos,
                Some(pos) => pos,
            };
            let actual = add_cursor(&before, new_pos);
            assert_eq_text!(after, &actual);
        }

        do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
        do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}");
        do_check("fn main() { <|>|x: i32| x * 2;}", "fn main() { |x: i32<|>| x * 2;}");

        {
            mark::check!(pipes_not_braces);
            do_check(
                "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
                "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
            );
        }
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.room.name* event.

use ruma_events_macros::EventContent;
use serde::{Deserialize, Serialize};

use crate::{InvalidInput, StateEvent};

/// The room name is a human-friendly string designed to be displayed to the end-user.
pub type NameEvent = StateEvent<NameEventContent>;

/// The payload for `NameEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.room.name", kind = State)]
pub struct NameEventContent {
    /// The name of the room. This MUST NOT exceed 255 bytes.
    #[serde(default, deserialize_with = "room_name")]
    pub(crate) name: Option<String>,
}

impl NameEventContent {
    /// Create a new `NameEventContent` with the given name.
    ///
    /// # Errors
    ///
    /// `InvalidInput` will be returned if the name is more than 255 bytes.
    pub fn new(name: String) -> Result<Self, InvalidInput> {
        match name.len() {
            0 => Ok(Self { name: None }),
            1..=255 => Ok(Self { name: Some(name) }),
            _ => Err(InvalidInput("a room name cannot be more than 255 bytes".into())),
        }
    }

    /// The name of the room, if any.
    pub fn name(&self) -> Option<&str> {
        self.name.as_deref()
    }
}

fn room_name<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    use serde::de::Error;

    // this handles the null case and the empty string or nothing case
    match Option::<String>::deserialize(deserializer)? {
        Some(name) => match name.len() {
            0 => Ok(None),
            1..=255 => Ok(Some(name)),
            _ => Err(D::Error::custom("a room name cannot be more than 255 bytes")),
        },
        None => Ok(None),
    }
}

#[cfg(test)]
mod tests {
    use js_int::{int, uint};
    use matches::assert_matches;
    use ruma_common::MilliSecondsSinceUnixEpoch;
    use ruma_identifiers::{event_id, room_id, user_id};
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::NameEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let name_event = StateEvent {
            content: NameEventContent { name: Some("The room name".into()) },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)),
            prev_content: None,
            room_id: room_id!("!n8f893n9:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_all_fields() {
        let name_event = StateEvent {
            content: NameEventContent { name: Some("The room name".into()) },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)),
            prev_content: Some(NameEventContent { name: Some("The old name".into()) }),
            room_id: room_id!("!n8f893n9:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned { age: Some(int!(100)), ..Unsigned::default() },
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "prev_content": { "name": "The old name" },
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name",
            "unsigned": {
                "age": 100
            }
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn name_fails_validation_when_too_long() {
        // "XXXX .." 256 times
        let long_string: String = std::iter::repeat('X').take(256).collect();
        assert_eq!(long_string.len(), 256);

        let long_content_json = json!({ "name": &long_string });
        let from_raw: Raw<NameEventContent> = from_json_value(long_content_json).unwrap();

        let result = from_raw.deserialize();
        assert!(result.is_err(), "Result should be invalid: {:?}", result);
    }

    #[test]
    fn json_with_empty_name_creates_content_as_none() {
        let long_content_json = json!({ "name": "" });
        let from_raw: Raw<NameEventContent> = from_json_value(long_content_json).unwrap();
        assert_matches!(from_raw.deserialize().unwrap(), NameEventContent { name: None });
    }

    #[test]
    fn new_with_empty_name_creates_content_as_none() {
        assert_matches!(
            NameEventContent::new(String::new()).unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "name": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn empty_string_as_none() {
        let json_data = json!({
            "content": {
                "name": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let name = Some("The room name".into());
        let json_data = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            name
        );
    }
}
<commit_msg>events: Use str::repeat instead of iter::repeat + collect<commit_after>//! Types for the *m.room.name* event.

use ruma_events_macros::EventContent;
use serde::{Deserialize, Serialize};

use crate::{InvalidInput, StateEvent};

/// The room name is a human-friendly string designed to be displayed to the end-user.
pub type NameEvent = StateEvent<NameEventContent>;

/// The payload for `NameEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.room.name", kind = State)]
pub struct NameEventContent {
    /// The name of the room. This MUST NOT exceed 255 bytes.
    #[serde(default, deserialize_with = "room_name")]
    pub(crate) name: Option<String>,
}

impl NameEventContent {
    /// Create a new `NameEventContent` with the given name.
    ///
    /// # Errors
    ///
    /// `InvalidInput` will be returned if the name is more than 255 bytes.
    pub fn new(name: String) -> Result<Self, InvalidInput> {
        match name.len() {
            0 => Ok(Self { name: None }),
            1..=255 => Ok(Self { name: Some(name) }),
            _ => Err(InvalidInput("a room name cannot be more than 255 bytes".into())),
        }
    }

    /// The name of the room, if any.
    pub fn name(&self) -> Option<&str> {
        self.name.as_deref()
    }
}

fn room_name<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    use serde::de::Error;

    // this handles the null case and the empty string or nothing case
    match Option::<String>::deserialize(deserializer)? {
        Some(name) => match name.len() {
            0 => Ok(None),
            1..=255 => Ok(Some(name)),
            _ => Err(D::Error::custom("a room name cannot be more than 255 bytes")),
        },
        None => Ok(None),
    }
}

#[cfg(test)]
mod tests {
    use js_int::{int, uint};
    use matches::assert_matches;
    use ruma_common::MilliSecondsSinceUnixEpoch;
    use ruma_identifiers::{event_id, room_id, user_id};
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::NameEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let name_event = StateEvent {
            content: NameEventContent { name: Some("The room name".into()) },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)),
            prev_content: None,
            room_id: room_id!("!n8f893n9:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_all_fields() {
        let name_event = StateEvent {
            content: NameEventContent { name: Some("The room name".into()) },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)),
            prev_content: Some(NameEventContent { name: Some("The old name".into()) }),
            room_id: room_id!("!n8f893n9:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned { age: Some(int!(100)), ..Unsigned::default() },
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "prev_content": { "name": "The old name" },
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name",
            "unsigned": {
                "age": 100
            }
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn name_fails_validation_when_too_long() {
        // "XXXX .." 256 times
        let long_string: String = "X".repeat(256);
        assert_eq!(long_string.len(), 256);

        let long_content_json = json!({ "name": &long_string });
        let from_raw: Raw<NameEventContent> = from_json_value(long_content_json).unwrap();

        let result = from_raw.deserialize();
        assert!(result.is_err(), "Result should be invalid: {:?}", result);
    }

    #[test]
    fn json_with_empty_name_creates_content_as_none() {
        let long_content_json = json!({ "name": "" });
        let from_raw: Raw<NameEventContent> = from_json_value(long_content_json).unwrap();
        assert_matches!(from_raw.deserialize().unwrap(), NameEventContent { name: None });
    }

    #[test]
    fn new_with_empty_name_creates_content_as_none() {
        assert_matches!(
            NameEventContent::new(String::new()).unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "name": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn empty_string_as_none() {
        let json_data = json!({
            "content": {
                "name": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let name = Some("The room name".into());
        let json_data = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(
            from_json_value::<Raw<StateEvent<NameEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            name
        );
    }
}
<|endoftext|>


<commit_before>use std::io::Buffer;

use time::{Tm, strptime};

use super::{HTTPLogRecord, LogProcessor, HTTPStatus};

pub struct NginxLogParser<B> {
    buffer: B
}

impl<R: Buffer> NginxLogParser<R> {
    pub fn new(buffer: R) -> NginxLogParser<R> {
        NginxLogParser{buffer: buffer}
    }

    pub fn parse<P: LogProcessor>(&mut self, processor: &mut P) {
        for result in self.buffer.lines() {
            let line = result.unwrap();
            let record = create_log_record(line.as_slice());
            processor.process(record);
        }
    }
}

#[inline]
fn create_log_record<'r>(line: &'r str) -> HTTPLogRecord<'r> {
    // TODO: Line parser can be implemented as iterator with a line
    // specification as input
    let (remote_addr, tail) = get_field(line);
    let (user, tail) = get_field(tail);
    let (local_time, tail) = get_local_time(tail);
    let (host, mut tail) = get_field(tail);
    // Pipe
    tail = skip_field(tail);
    let (request_time, tail) = get_request_time(tail);
    let (method, path, tail) = get_method_path(tail);
    let (status, tail) = get_int(tail);
    let (sent_bytes, tail) = get_int(tail);
    let (referer, tail) = get_delimited_field(tail, '"', '"');
    let (user_agent, _) = get_delimited_field(tail, '"', '"');
    HTTPLogRecord{
        remote_addr: remote_addr,
        local_time: local_time,
        host: host,
        user: user,
        request_time: request_time,
        method: method,
        path: path,
        status: HTTPStatus{status: status as u16},
        sent_bytes: sent_bytes,
        referer: referer,
        user_agent: user_agent,
        }
}

#[inline]
fn get_field<'a>(line: &'a str) -> (&'a str, &'a str) {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
        None => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_field_or<'a>(line: &'a str, default: &'a str) -> (&'a str, &'a str) {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
        None => (default, "")
    }
}

#[inline]
fn skip_field<'a>(line: &'a str) -> &'a str {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => slice.slice_from(end + 1),
        None => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_delimited_field<'a>(line: &'a str, start_c: char, end_c: char) ->
        (&'a str, &'a str) {
    match line.trim_left().slice_shift_char() {
        (Some(c), slice) if c == start_c =>
            // FIXME: Should we skip escaped end characters? But probably
            // not so important in this case
            match slice.find(end_c) {
                Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
                None => fail!("incomplete string: {}", line)
            },
        _ => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_local_time<'a>(line: &'a str) -> (Tm, &'a str) {
    let (slice, tail) = get_delimited_field(line, '[', ']');
    match strptime(slice, "%d/%b/%Y:%H:%M:%S %z") {
        Ok(local_time) => (local_time, tail),
        Err(err) => fail!("time parse error for {}: {}", line, err)
    }
}

#[inline]
fn get_request_time<'a>(line: &'a str) -> (u64, &'a str) {
    let (slice, tail) = get_field(line);
    match slice.find('.') {
        Some(pos) => {
            let sec: u64 = from_str(slice.slice_to(pos)).unwrap();
            let msec: u64 = from_str(slice.slice_from(pos + 1)).unwrap();
            (sec * 1000 + msec, tail)
        }
        None => fail!("invalid request time: {}", line)
    }
}

#[inline]
fn get_method_path<'a>(line: &'a str) -> (&'a str, &'a str, &'a str) {
    let (slice, tail) = get_delimited_field(line, '"', '"');
    let (method, req_tail) = get_field_or(slice, "");
    let (path, _) = get_field_or(req_tail, "");
    (method, path, tail)
}

#[inline]
fn get_int<'a>(line: &'a str) -> (u64, &'a str) {
    let (slice, tail) = get_field(line);
    (from_str(slice).unwrap(), tail)
}
<commit_msg>Remove unused 'use'<commit_after>use time::{Tm, strptime};

use super::{HTTPLogRecord, LogProcessor, HTTPStatus};

pub struct NginxLogParser<B> {
    buffer: B
}

impl<B: Buffer> NginxLogParser<B> {
    pub fn new(buffer: B) -> NginxLogParser<B> {
        NginxLogParser{buffer: buffer}
    }

    pub fn parse<P: LogProcessor>(&mut self, processor: &mut P) {
        for result in self.buffer.lines() {
            let line = result.unwrap();
            let record = create_log_record(line.as_slice());
            processor.process(record);
        }
    }
}

#[inline]
fn create_log_record<'r>(line: &'r str) -> HTTPLogRecord<'r> {
    // TODO: Line parser can be implemented as iterator with a line
    // specification as input
    let (remote_addr, tail) = get_field(line);
    let (user, tail) = get_field(tail);
    let (local_time, tail) = get_local_time(tail);
    let (host, mut tail) = get_field(tail);
    // Pipe
    tail = skip_field(tail);
    let (request_time, tail) = get_request_time(tail);
    let (method, path, tail) = get_method_path(tail);
    let (status, tail) = get_int(tail);
    let (sent_bytes, tail) = get_int(tail);
    let (referer, tail) = get_delimited_field(tail, '"', '"');
    let (user_agent, _) = get_delimited_field(tail, '"', '"');
    HTTPLogRecord{
        remote_addr: remote_addr,
        local_time: local_time,
        host: host,
        user: user,
        request_time: request_time,
        method: method,
        path: path,
        status: HTTPStatus{status: status as u16},
        sent_bytes: sent_bytes,
        referer: referer,
        user_agent: user_agent,
        }
}

#[inline]
fn get_field<'a>(line: &'a str) -> (&'a str, &'a str) {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
        None => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_field_or<'a>(line: &'a str, default: &'a str) -> (&'a str, &'a str) {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
        None => (default, "")
    }
}

#[inline]
fn skip_field<'a>(line: &'a str) -> &'a str {
    let slice = line.trim_left();
    match slice.find(' ') {
        Some(end) => slice.slice_from(end + 1),
        None => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_delimited_field<'a>(line: &'a str, start_c: char, end_c: char) ->
        (&'a str, &'a str) {
    match line.trim_left().slice_shift_char() {
        (Some(c), slice) if c == start_c =>
            // FIXME: Should we skip escaped end characters? But probably
            // not so important in this case
            match slice.find(end_c) {
                Some(end) => (slice.slice_to(end), slice.slice_from(end + 1)),
                None => fail!("incomplete string: {}", line)
            },
        _ => fail!("incomplete string: {}", line)
    }
}

#[inline]
fn get_local_time<'a>(line: &'a str) -> (Tm, &'a str) {
    let (slice, tail) = get_delimited_field(line, '[', ']');
    match strptime(slice, "%d/%b/%Y:%H:%M:%S %z") {
        Ok(local_time) => (local_time, tail),
        Err(err) => fail!("time parse error for {}: {}", line, err)
    }
}

#[inline]
fn get_request_time<'a>(line: &'a str) -> (u64, &'a str) {
    let (slice, tail) = get_field(line);
    match slice.find('.') {
        Some(pos) => {
            let sec: u64 = from_str(slice.slice_to(pos)).unwrap();
            let msec: u64 = from_str(slice.slice_from(pos + 1)).unwrap();
            (sec * 1000 + msec, tail)
        }
        None => fail!("invalid request time: {}", line)
    }
}

#[inline]
fn get_method_path<'a>(line: &'a str) -> (&'a str, &'a str, &'a str) {
    let (slice, tail) = get_delimited_field(line, '"', '"');
    let (method, req_tail) = get_field_or(slice, "");
    let (path, _) = get_field_or(req_tail, "");
    (method, path, tail)
}

#[inline]
fn get_int<'a>(line: &'a str) -> (u64, &'a str) {
    let (slice, tail) = get_field(line);
    (from_str(slice).unwrap(), tail)
}
<|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/. */

extern crate app_units;
extern crate cssparser;
extern crate gecko_bindings;
extern crate style;
extern crate style_traits;

mod sanity_checks;
<commit_msg>Auto merge of #13528 - UK992:test-geckolib, r=Manishearth<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/. */

extern crate app_units;
extern crate cssparser;
extern crate style;
extern crate style_traits;

mod sanity_checks;
<|endoftext|>


<commit_before>#[cfg(target_os = "linux")]
extern crate i2cdev;
extern crate termion;

#[cfg(target_os = "linux")]
use self::i2cdev::core::I2CDevice;
#[cfg(target_os = "linux")]
use self::i2cdev::linux::LinuxI2CDevice;

use std;
use types::*;

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

pub trait Projector {
    fn project(&mut self, &[Column]);
}

#[cfg(target_os = "linux")]
pub struct I2CProjector {
    device: LinuxI2CDevice,
    frame: u8,
}

#[cfg(target_os = "linux")]
impl I2CProjector {
    pub fn new() -> I2CProjector {
        let d = LinuxI2CDevice::new("/dev/i2c-1", ADDRESS).unwrap();
        // self.register(CONFIG_BANK, MODE_REGISTER, PICTURE_MODE);
        I2CProjector {
            device: d,
            frame: 0,
        }
    }

    fn bank(&mut self, bank: u8) {
        self.device.smbus_write_byte_data(BANK_ADDRESS, bank).unwrap();
    }

    fn register(&mut self, bank: u8, register: u8, value: u8) {
        self.bank(bank);
        self.device.smbus_write_block_data(register, &[value]).unwrap();
    }

    fn frame(&mut self, frame: u8) {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame);
    }

    fn reset(&mut self) {
        self.sleep(true);
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false);
    }

    fn sleep(&mut self, value: bool) {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 });
    }
}

#[cfg(target_os = "linux")]
impl Projector for I2CProjector {
    fn project(&mut self, buffer: &[Column]) {
        // TODO(tzn): Double buffering.
        // let new_frame = (self.frame + 1) % 2;
        let new_frame = 1;
        self.bank(new_frame);
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.device
                    .smbus_write_byte_data(COLOR_OFFSET + offset as u8, value)
                    .unwrap();
            }
        }
        self.frame(new_frame);
        self.frame = new_frame;
    }
}

pub struct TermProjector {}

impl TermProjector {
    pub fn new() -> TermProjector {
        TermProjector {}
    }
}

impl Projector for TermProjector {
    fn project(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);
        for x in 0..buffer.len() {
            let col = &buffer[x];
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { ' ' } else { '#' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 1, y as u16 + 1), v);
            }
        }
    }
}
<commit_msg>Allow dead code<commit_after>#![allow(dead_code)]

#[cfg(target_os = "linux")]
extern crate i2cdev;
extern crate termion;

#[cfg(target_os = "linux")]
use self::i2cdev::core::I2CDevice;
#[cfg(target_os = "linux")]
use self::i2cdev::linux::LinuxI2CDevice;

use std;
use types::*;

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

pub trait Projector {
    fn project(&mut self, &[Column]);
}

#[cfg(target_os = "linux")]
pub struct I2CProjector {
    device: LinuxI2CDevice,
    frame: u8,
}

#[cfg(target_os = "linux")]
impl I2CProjector {
    pub fn new() -> I2CProjector {
        let d = LinuxI2CDevice::new("/dev/i2c-1", ADDRESS).unwrap();
        // self.register(CONFIG_BANK, MODE_REGISTER, PICTURE_MODE);
        I2CProjector {
            device: d,
            frame: 0,
        }
    }

    fn bank(&mut self, bank: u8) {
        self.device.smbus_write_byte_data(BANK_ADDRESS, bank).unwrap();
    }

    fn register(&mut self, bank: u8, register: u8, value: u8) {
        self.bank(bank);
        self.device.smbus_write_block_data(register, &[value]).unwrap();
    }

    fn frame(&mut self, frame: u8) {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame);
    }

    fn reset(&mut self) {
        self.sleep(true);
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false);
    }

    fn sleep(&mut self, value: bool) {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 });
    }
}

#[cfg(target_os = "linux")]
impl Projector for I2CProjector {
    fn project(&mut self, buffer: &[Column]) {
        // TODO(tzn): Double buffering.
        // let new_frame = (self.frame + 1) % 2;
        let new_frame = 1;
        self.bank(new_frame);
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.device
                    .smbus_write_byte_data(COLOR_OFFSET + offset as u8, value)
                    .unwrap();
            }
        }
        self.frame(new_frame);
        self.frame = new_frame;
    }
}

pub struct TermProjector {}

impl TermProjector {
    pub fn new() -> TermProjector {
        TermProjector {}
    }
}

impl Projector for TermProjector {
    fn project(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);
        for x in 0..buffer.len() {
            let col = &buffer[x];
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { ' ' } else { '#' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 1, y as u16 + 1), v);
            }
        }
    }
}
<|endoftext|>


<commit_before>
use std::io;
use std::io::prelude::*;
use job::jobs;
use agent::client;

pub fn start() {
    let mut stdout = io::stdout();
    loop {

        write!(&mut stdout, "builr> ");
        stdout.flush();

        let input = &mut String::new();
        match io::stdin().read_line(input) {
            Ok(x)=> {
                let input = input.replace("\n", "");
                if input.len() > 0 {
                    println!("{:?} {:?}", input, x);

                    if "jobs" == input {
                        jobs::list();
                    }
                    else if "ping" == input {
                        client::ping(4);
                    }
                }
            },
            Err(e) => {
                panic!("{}", e);
            }
        }

    }
}

<commit_msg>Exit if quit or exit are entered on the REPL<commit_after>
use std::io;
use std::io::prelude::*;
use std::process;
use job::jobs;
use agent::client;

pub fn start() {
    let mut stdout = io::stdout();
    loop {

        write!(&mut stdout, "builr> ");
        stdout.flush();

        let input = &mut String::new();
        match io::stdin().read_line(input) {
            Ok(x)=> {
                let input = input.replace("\n", "");
                if input.len() > 0 {
                    println!("{:?} {:?}", input, x);

                    if "jobs" == input {
                        jobs::list();
                    }
                    else if "ping" == input {
                        client::ping(4);
                    }
                    else if "exit" == input || "quit" == input {
                        process::exit(0);
                    }
                }
            },
            Err(e) => {
                panic!("{}", e);
            }
        }

    }
}

<|endoftext|>