<commit_before>#[macro_use]
extern crate prodbg_api;

use prodbg_api::*;

struct Line {
    opcode: String,
    regs_write: String,
    regs_read: String,
    address: u64,
}

///
/// Breakpoint
///
struct Breakpoint {
    address: u64,
}

///
/// Holds colors use for the disassembly view. This should be possible to configure later on.
///
/*
struct Colors {
    breakpoint: Color,
    step_cursor: Color,
    cursor: Color,
    address: Color,
    _bytes: Color,
}
*/

struct DisassemblyView {
    location: u64,
    _cursor: u64,
    breakpoint_radius: f32,
    breakpoint_spacing: f32,
    address_size: u8,
    reset_to_center: bool,
    lines: Vec<Line>,
    breakpoints: Vec<Breakpoint>,
}

impl DisassemblyView {
    fn set_disassembly(&mut self, reader: &mut Reader) {
        self.lines.clear();

        for entry in reader.find_array("disassembly") {
            let address = entry.find_u64("address").ok().unwrap();
            let line = entry.find_string("line").ok().unwrap();
            let mut regs_read = String::new();
            let mut regs_write = String::new();

            entry.find_string("registers_read").map(|regs| {
                regs_read = regs.to_owned();
            }).ok();

            entry.find_string("registers_write").map(|regs| {
                regs_write = regs.to_owned();
            }).ok();

            self.lines.push(Line {
                opcode: line.to_owned(),
                regs_read: regs_read,
                regs_write: regs_write,
                address: address,
            });
        }
    }

    ///
    /// Calculate how many visible lines we have
    ///
    fn get_visible_lines_count(ui: &Ui) -> usize {
        let (_, height) = ui.get_window_size();
        let text_height = ui.get_text_line_height_with_spacing();
        // - 1.0 for title text. Would be better to get the cursor pos here instead
        let visible_lines = (height / text_height) - 1.0;
        // + 0.5 to round up
        (visible_lines + 0.5) as usize
    }

    fn request_disassembly(&mut self, ui: &mut Ui, location: u64, writer: &mut Writer) {
        let visible_lines = Self::get_visible_lines_count(ui) as u64;

        self.location = location;

        // check if we have the the location within all lines, then we don't need to request more
        for line in &self.lines {
            if line.address == location {
                return;
            }
        }

        self.reset_to_center = true;
        writer.event_begin(EVENT_GET_DISASSEMBLY as u16);
        writer.write_u64("address_start", location - (visible_lines * 4));
        writer.write_u32("instruction_count", (visible_lines * 4) as u32);
        writer.event_end();
        println!("requsted {}", visible_lines * 10);
    }

    fn color_text_reg_selection(ui: &Ui, regs_use: &Vec<&str>, line: &Line, text_height: f32) {
        let (cx, cy) = ui.get_cursor_screen_pos();
        let mut color_index = 0;
        // TODO: Allocs memory, fix
        let line_text = format!("0x{:x} {}", line.address, line.opcode);

        let colors = [
            0x00b27474,
            0x00b28050,
            0x00a9b250,
            0x0060b250,

            0x004fb292,
            0x004f71b2,
            0x008850b2,
            0x00b25091,
        ];

        //let mut color_index = 0;

        //let font_size = 14.0;
        // TODO: Offset here is hardcoded with given hex size, this should be fixed
        //let start_offset = font_size * 11.0; // 0x00000000 "

        for reg in regs_use {
            let color = colors[color_index & 7];
            line_text.find(reg).map(|offset| {
                let (tx, _) = ui.calc_text_size(&line_text, offset);
                ui.fill_rect(cx + tx, cy, 22.0, text_height, Color::from_au32(200, color));
            });

            color_index += 1;
        }

        ui.text(&line_text);
    }

    fn toggle_breakpoint(&mut self, writer: &mut Writer) {
        // TODO: Should really be cursor_pos
        let address = self.location;

        for i in (0..self.breakpoints.len()).rev() {
            if self.breakpoints[i].address == address {
                writer.event_begin(EVENT_DELETE_BREAKPOINT as u16);
                writer.write_u64("address", address);
                writer.event_end();
                self.breakpoints.swap_remove(i);
                return;
            }
        }

        writer.event_begin(EVENT_SET_BREAKPOINT as u16);
        writer.write_u64("address", address);
        writer.event_end();

        // TODO: We shouldn't really add the breakpoint here but wait for reply
        // from the backend that we actually managed to set the breakpoint.
        // +bonus would be a "progress" icon here instead that it's being set.

        self.breakpoints.push(Breakpoint { address: self.location } );
    }

    fn has_breakpoint(&self, address: u64) -> bool {
        self.breakpoints.iter().find(|bp| bp.address == address).is_some()
    }

    fn render_ui(&mut self, ui: &mut Ui) {
        if self.lines.len() == 0 {
            return;
        }

        let (size_x, size_h) = ui.get_window_size();
        let text_height = ui.get_text_line_height_with_spacing();
        let mut regs = String::new();
        let mut regs_pc_use = Vec::new();
        //let font_size = ui.get_font_size();

        // find registerss for pc

        for line in &self.lines {
            if line.address == self.location {
                if line.regs_read.len() > 1 || line.regs_write.len() > 1 {
                    if line.regs_read.len() > 0 {
                        regs.push_str(&line.regs_read);
                    }
                    if line.regs_write.len() > 0 {
                        regs.push(' ');
                        regs.push_str(&line.regs_write);
                    }

                    let t = regs.trim_left();
                    regs_pc_use = t.split(' ').collect();
                    break;
                }
            }
        }

        for line in &self.lines {
            let (cx, cy) = ui.get_cursor_screen_pos();
            let bp_radius = self.breakpoint_radius;

            if line.address == self.location {
                if (cy < 0.0 || cy > (size_h - text_height)) || self.reset_to_center {
                    ui.set_scroll_here(0.5);
                    self.reset_to_center = false;
                }

                ui.fill_rect(cx, cy, size_x, text_height, Color::from_argb(200, 0, 0, 127));
            }

            if regs_pc_use.len() > 0 {
                Self::color_text_reg_selection(ui, &regs_pc_use, &line, text_height);
            } else {
                ui.text_fmt(format_args!("   0x{:x} {}", line.address, line.opcode));
            }

            if self.has_breakpoint(line.address) {
                    ui.fill_circle(&Vec2{ x: cx + self.breakpoint_spacing + bp_radius, y: cy + bp_radius + 2.0},
                                   bp_radius, Color::from_argb(255,0,0,140), 12, false);
            }
        }

        /*
        let arrow: [Vec2; 3] = [
            Vec2::new(112.0, 122.0),
            Vec2::new(212.0, 222.0),
            Vec2::new(12.0, 322.0),
        ];
        */

        //ui.fill_convex_poly(&arrow, Color::from_argb(255,0,0,200), true);
        //ui.fill_circle(&Vec2{ x: 112.0, y: 112.0}, 12.0, Color::from_argb(255,0,0,200), 12, false);
        //ui.fill_circle(&Vec2{ x: 112.0, y: 112.0}, 12.0, Color::from_argb(255,0,0,200), 12, false);
    }
}

impl View for DisassemblyView {
    fn new(_: &Ui, _: &Service) -> Self {
        DisassemblyView {
            location: u64::max_value(),
            _cursor: u64::max_value(),
            breakpoint_radius: 10.0,
            breakpoint_spacing: 6.0,
            address_size: 4,
            lines: Vec::new(),
            breakpoints: Vec::new(),
            reset_to_center: false,
        }
    }

    fn update(&mut self, ui: &mut Ui, reader: &mut Reader, writer: &mut Writer) {
        for event in reader.get_events() {
            match event {
                EVENT_SET_EXCEPTION_LOCATION => {
                    let location = reader.find_u64("address").ok().unwrap();

                    reader.find_u8("address_size").ok().map(|adress_size| {
                        self.address_size = adress_size;
                    });

                    if self.location != location {
                        self.request_disassembly(ui, location, writer);
                    }
                }

                EVENT_SET_DISASSEMBLY => {
                    self.set_disassembly(reader);
                }

                _ => (),
            }
        }

        if ui.is_key_down(Key::F9) {
            self.toggle_breakpoint(writer);
        }

        self.render_ui(ui);
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_view_plugin!(PLUGIN, b"Disassembly2 View", DisassemblyView);
    plugin_handler.register_view(&PLUGIN);
}
<commit_msg>Much improved disassembly view<commit_after>#[macro_use]
extern crate prodbg_api;

use prodbg_api::*;

struct Line {
    opcode: String,
    regs_write: String,
    regs_read: String,
    address: u64,
}

///
/// Breakpoint
///
struct Breakpoint {
    address: u64,
}

///
/// Holds colors use for the disassembly view. This should be possible to configure later on.
///
/*
struct Colors {
    breakpoint: Color,
    step_cursor: Color,
    cursor: Color,
    address: Color,
    _bytes: Color,
}
*/

struct DisassemblyView {
    exception_location: u64,
    has_made_step: bool,
    cursor: u64,
    breakpoint_radius: f32,
    breakpoint_spacing: f32,
    address_size: u8,
    reset_to_center: bool,
    lines: Vec<Line>,
    breakpoints: Vec<Breakpoint>,
}

impl DisassemblyView {
    fn set_disassembly(&mut self, reader: &mut Reader) {
        self.lines.clear();

        for entry in reader.find_array("disassembly") {
            let address = entry.find_u64("address").ok().unwrap();
            let line = entry.find_string("line").ok().unwrap();
            let mut regs_read = String::new();
            let mut regs_write = String::new();

            entry.find_string("registers_read").map(|regs| {
                regs_read = regs.to_owned();
            }).ok();

            entry.find_string("registers_write").map(|regs| {
                regs_write = regs.to_owned();
            }).ok();

            self.lines.push(Line {
                opcode: line.to_owned(),
                regs_read: regs_read,
                regs_write: regs_write,
                address: address,
            });
        }
    }

    ///
    /// Calculate how many visible lines we have
    ///
    fn get_visible_lines_count(ui: &Ui) -> usize {
        let (_, height) = ui.get_window_size();
        let text_height = ui.get_text_line_height_with_spacing();
        // - 1.0 for title text. Would be better to get the cursor pos here instead
        let visible_lines = (height / text_height) - 1.0;
        // + 0.5 to round up
        (visible_lines + 0.5) as usize
    }

    fn request_disassembly(&mut self, ui: &mut Ui, location: u64, writer: &mut Writer) {
        let visible_lines = Self::get_visible_lines_count(ui) as u64;

        // check if we have the the location within all lines, then we don't need to request more
        for line in &self.lines {
            if line.address == location {
                return;
            }
        }

        self.reset_to_center = true;
        writer.event_begin(EVENT_GET_DISASSEMBLY as u16);
        writer.write_u64("address_start", location - (visible_lines * 4));
        writer.write_u32("instruction_count", (visible_lines * 4) as u32);
        writer.event_end();
        println!("requsted {}", visible_lines * 10);
    }

    fn color_text_reg_selection(ui: &Ui, regs_use: &Vec<&str>, line: &Line, text_height: f32) {
        let (cx, cy) = ui.get_cursor_screen_pos();
        let mut color_index = 0;
        // TODO: Allocs memory, fix
        let line_text = format!("   0x{:x} {}", line.address, line.opcode);

        let colors = [
            0x00b27474,
            0x00b28050,
            0x00a9b250,
            0x0060b250,

            0x004fb292,
            0x004f71b2,
            0x008850b2,
            0x00b25091,
        ];

        //let mut color_index = 0;

        //let font_size = 14.0;
        // TODO: Offset here is hardcoded with given hex size, this should be fixed
        //let start_offset = font_size * 11.0; // 0x00000000 "

        for reg in regs_use {
            let color = colors[color_index & 7];
            line_text.find(reg).map(|offset| {
                let (tx, _) = ui.calc_text_size(&line_text, offset);
                ui.fill_rect(cx + tx, cy, 22.0, text_height, Color::from_au32(200, color));
            });

            color_index += 1;
        }

        ui.text(&line_text);
    }

    fn toggle_breakpoint(&mut self, writer: &mut Writer) {
        let address = self.cursor;

        for i in (0..self.breakpoints.len()).rev() {
            if self.breakpoints[i].address == address {
                writer.event_begin(EVENT_DELETE_BREAKPOINT as u16);
                writer.write_u64("address", address);
                writer.event_end();
                self.breakpoints.swap_remove(i);
                return;
            }
        }

        writer.event_begin(EVENT_SET_BREAKPOINT as u16);
        writer.write_u64("address", address);
        writer.event_end();

        println!("adding breakpoint");

        // TODO: We shouldn't really add the breakpoint here but wait for reply
        // from the backend that we actually managed to set the breakpoint.
        // +bonus would be a "progress" icon here instead that it's being set.

        self.breakpoints.push(Breakpoint { address: address } );
    }

    fn has_breakpoint(&self, address: u64) -> bool {
        self.breakpoints.iter().find(|bp| bp.address == address).is_some()
    }

    /*
    fn set_cursor_at(&mut self, pos: i32) {
        // if we don'n have enough lines we need to fetch more
        if pos < 0 {

            return;
        }

        // need to fetch more data here also
        if pos > self.lines.len() as i32 {

            return;
        }

        self.cursor = self.lines[pos as usize].address;
    }
    */

    fn scroll_cursor(&mut self, steps: i32) {
        for (i, line) in self.lines.iter().enumerate() {
            if line.address == self.cursor {
                let pos = (i as i32) + steps;
                // if we don'n have enough lines we need to fetch more
                if pos < 0 {
                    return;
                }

                // need to fetch more data here also
                if pos >= self.lines.len() as i32 {
                    return;
                }

                self.cursor = self.lines[pos as usize].address;
                return;
            }
        }
    }

    fn render_arrow(ui: &Ui, pos_x: f32, pos_y: f32, scale: f32) {
        let color = Color::from_argb(255, 0, 180, 180);

        ui.fill_rect(pos_x + (0.0 * scale),
                     pos_y + (0.25 * scale),
                     0.5 * scale,
                     0.5 * scale,
                     color);

        // Wasn't able to get this to work with just one convex poly fill
        let arrow: [Vec2; 3] = [
            Vec2::new((0.50 * scale) + pos_x, (0.00 * scale) + pos_y),
            Vec2::new((1.00 * scale) + pos_x, (0.50 * scale) + pos_y),
            Vec2::new((0.50 * scale) + pos_x, (1.00 * scale) + pos_y),
        ];

        ui.fill_convex_poly(&arrow, color, true);
    }

    fn render_ui(&mut self, ui: &mut Ui) {
        if self.lines.len() == 0 {
            return;
        }

        let (size_x, size_h) = ui.get_window_size();
        let text_height = ui.get_text_line_height_with_spacing();
        let mut regs = String::new();
        let mut regs_pc_use = Vec::new();
        //let font_size = ui.get_font_size();

        // find registerss for pc

        for line in &self.lines {
            if line.address == self.exception_location {
                if line.regs_read.len() > 1 || line.regs_write.len() > 1 {
                    if line.regs_read.len() > 0 {
                        regs.push_str(&line.regs_read);
                    }
                    if line.regs_write.len() > 0 {
                        regs.push(' ');
                        regs.push_str(&line.regs_write);
                    }

                    let t = regs.trim_left();
                    regs_pc_use = t.split(' ').collect();
                    break;
                }
            }
        }

        for line in &self.lines {
            let (cx, cy) = ui.get_cursor_screen_pos();
            let bp_radius = self.breakpoint_radius;

            if line.address == self.cursor {
                if (cy - text_height) < 0.0 {
                    if self.reset_to_center || self.has_made_step {
                        ui.set_scroll_here(0.5);
                        self.reset_to_center = false;
                    } else {
                        ui.set_scroll_from_pos_y(cy - text_height, 0.0);
                    }
                }

                if cy > (size_h - text_height) {
                    if self.reset_to_center || self.has_made_step {
                        ui.set_scroll_here(0.5);
                        self.reset_to_center = false;
                    } else {
                        ui.set_scroll_from_pos_y(cy + text_height, 1.0);
                    }
                }

                ui.fill_rect(cx, cy, size_x, text_height, Color::from_argb(200, 0, 0, 127));
            }

            if regs_pc_use.len() > 0 {
                Self::color_text_reg_selection(ui, &regs_pc_use, &line, text_height);
            } else {
                ui.text_fmt(format_args!("   0x{:x} {}", line.address, line.opcode));
            }

            if self.has_breakpoint(line.address) {
                    ui.fill_circle(&Vec2{ x: cx + self.breakpoint_spacing + bp_radius, y: cy + bp_radius + 2.0},
                                   bp_radius, Color::from_argb(255,0,0,140), 12, false);
            }

            //println!("draw arrow {} {}", line.address, self.exception_location);

            if line.address == self.exception_location {
                Self::render_arrow(ui, cx, cy + 2.0, self.breakpoint_radius * 2.0);
            }
        }

        self.has_made_step = false;
    }
}

impl View for DisassemblyView {
    fn new(_: &Ui, _: &Service) -> Self {
        DisassemblyView {
            exception_location: u64::max_value(),
            cursor: 0xe003, //u64::max_value(),
            breakpoint_radius: 10.0,
            breakpoint_spacing: 6.0,
            address_size: 4,
            has_made_step: false,
            lines: Vec::new(),
            breakpoints: Vec::new(),
            reset_to_center: false,
        }
    }

    fn update(&mut self, ui: &mut Ui, reader: &mut Reader, writer: &mut Writer) {
        for event in reader.get_events() {
            match event {
                EVENT_SET_EXCEPTION_LOCATION => {
                    let location = reader.find_u64("address").ok().unwrap();

                    reader.find_u8("address_size").ok().map(|adress_size| {
                        self.address_size = adress_size;
                    });

                    if self.exception_location != location {
                        self.request_disassembly(ui, location, writer);
                        self.has_made_step = true;
                        self.exception_location = location;
                        self.cursor = location;
                    }
                }

                EVENT_SET_DISASSEMBLY => {
                    self.set_disassembly(reader);
                }

                _ => (),
            }
        }

        if ui.is_key_down(Key::F9) {
            self.toggle_breakpoint(writer);
        }

        if ui.is_key_down(Key::Down) {
            self.scroll_cursor(1);
        }

        if ui.is_key_down(Key::Up) {
            self.scroll_cursor(-1);
        }

        if ui.is_key_down(Key::PageDown) {
            self.scroll_cursor(8);
        }

        if ui.is_key_down(Key::PageUp) {
            self.scroll_cursor(-8);
        }

        self.render_ui(ui);
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_view_plugin!(PLUGIN, b"Disassembly2 View", DisassemblyView);
    plugin_handler.register_view(&PLUGIN);
}
<|endoftext|>


<commit_before>extern crate vk_api;
extern crate xml;

mod crawler;

use xml::EventReader;
use xml::attribute::OwnedAttribute;

use std::os::raw::c_char;
use std::ffi::CStr;
use std::ptr;
use std::fmt;

pub fn load_xml() {
    let vk_xml = EventReader::new(vk_api::VK_XML);
    // The vulkan registry
    let registry = crawler::crawl(vk_xml.into_iter(), VkRegistry::new(vk_api::VK_XML.len()));
}

pub struct VkRegistry {
    string_buffer: String,
    types: Vec<VkType>
}

impl<'a> VkRegistry {
    fn new(capacity: usize) -> VkRegistry {
        VkRegistry {
            string_buffer: String::with_capacity(capacity),
            types: Vec::with_capacity(256)
        }
    }

    fn push_type(&mut self, vk_type: VkType) -> Result<(), ()> {
        match vk_type {
            VkType::Unhandled => Err(()),
            vk_type           => {self.types.push(vk_type); Ok(())}
        }
    }

    /// Append a given attribute to the internal string buffer and return a pointer to a
    /// null-terminated string that represents the attribute
    fn append_str(&mut self, string: &str) -> *const c_char {
        // We want to have all of the string in one block of memory in order to save heap allocation time.
        self.string_buffer.push_str(string);
        self.string_buffer.push('\0');

        (self.string_buffer.as_ptr() as usize + self.string_buffer.len() - string.len() - 1) as *const c_char
    }
}

enum VkMemberType {
    Var(*const c_char),
    Const(*const c_char),
    ConstPtr(*const c_char),
    MutPtr(*const c_char),
    /// Default value to initialize with.
    Unknown
}

impl fmt::Debug for VkMemberType {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use VkMemberType::*;
        if let Unknown = *self {
            write!(fmt, "Unknown")
        } else {
            let pt;
            fmt.debug_tuple(
                match *self {
                    Var(p) => {pt = p; "Var"}
                    Const(p) => {pt = p; "Const"}
                    ConstPtr(p) => {pt = p; "ConstPtr"}
                    MutPtr(p) => {pt = p; "MutPtr"}
                    Unknown => unreachable!()
                })
                .field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(pt).to_str().unwrap()} else {"Error: Null Ptr"} })
                .finish()
        }
    }
}

struct VkMember {
    field_type: VkMemberType,
    field_name: *const c_char,
}

impl fmt::Debug for VkMember {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt .debug_struct("VkMember")
            .field("type", &self.field_type)
            .field("name", unsafe{ &CStr::from_ptr(self.field_name) })
            .finish()
    }
}

impl VkMember {
    fn empty() -> VkMember {
        VkMember {
            field_type: VkMemberType::Unknown,
            field_name: ptr::null()
        }
    }

    fn type_ptr(&self) -> Option<*const c_char> {
        use VkMemberType::*;
        match self.field_type {
            Var(s) |
            Const(s) |
            ConstPtr(s) |
            MutPtr(s) => Some(s),
            Unknown => None
        }
    }

    fn set_type(&mut self, field_type: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(ref mut s) |
            Const(ref mut s) |
            ConstPtr(ref mut s) |
            MutPtr(ref mut s) =>
                if *s == ptr::null() {
                    *s = field_type
                } else {panic!("Field type already set")},
            Unknown => self.field_type = Var(field_type)

        }
    }

    fn change_type_var(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s) |
            Const(s) |
            ConstPtr(s) |
            MutPtr(s) => self.field_type = Var(s),
            Unknown   => self.field_type = Var(ptr::null())
        }
    }

    fn change_type_const(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s) |
            Const(s) => self.field_type = Const(s),
            ConstPtr(s) |
            MutPtr(s) => panic!("Attempted changing mutability of pointer"),
            Unknown   => self.field_type = Const(ptr::null())
        }
    }

    fn change_type_ptr(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Const(s) |
            ConstPtr(s) => self.field_type = ConstPtr(s),
            Var(s) |
            MutPtr(s) => self.field_type = MutPtr(s),
            Unknown   => self.field_type = MutPtr(ptr::null())
        }
    }

    fn set_name(&mut self, field_name: *const c_char) {
        if self.field_name != ptr::null() {
            panic!("Unexpected \"name\" tag");
        } else {
            self.field_name = field_name;
        }
    }
}

/// A variant of a vulkan enum
#[derive(Debug)]
struct VkVariant {
    name: *const c_char,
    value: isize
}

#[derive(Debug)]
enum VkType {
    Struct {
        name: *const c_char,
        fields: Vec<VkMember>
    },

    Union {
        name: *const c_char,
        variants: Vec<VkMember>
    },

    Enum {
        name: *const c_char,
        variants: Vec<VkVariant>
    },

    BaseType {
        /// The type that is being aliased
        ty: *const c_char,
        /// The name of the new type
        name: *const c_char
    },

    Unhandled
}<commit_msg>Added ConstArray and MutArray to VkMember, updated VkMember functions to perform some validation.<commit_after>extern crate vk_api;
extern crate xml;

mod crawler;

use xml::EventReader;
use xml::attribute::OwnedAttribute;

use std::os::raw::c_char;
use std::ffi::CStr;
use std::ptr;
use std::fmt;

pub fn load_xml() {
    let vk_xml = EventReader::new(vk_api::VK_XML);
    // The vulkan registry
    let registry = crawler::crawl(vk_xml.into_iter(), VkRegistry::new(vk_api::VK_XML.len()));
}

pub struct VkRegistry {
    string_buffer: String,
    types: Vec<VkType>
}

impl<'a> VkRegistry {
    fn new(capacity: usize) -> VkRegistry {
        VkRegistry {
            string_buffer: String::with_capacity(capacity),
            types: Vec::with_capacity(256)
        }
    }

    fn push_type(&mut self, vk_type: VkType) -> Result<(), ()> {
        match vk_type {
            VkType::Unhandled => Err(()),
            vk_type           => {self.types.push(vk_type); Ok(())}
        }
    }

    /// Append a given attribute to the internal string buffer and return a pointer to a
    /// null-terminated string that represents the attribute
    fn append_str(&mut self, string: &str) -> *const c_char {
        // We want to have all of the string in one block of memory in order to save heap allocation time.
        self.string_buffer.push_str(string);
        self.string_buffer.push('\0');

        (self.string_buffer.as_ptr() as usize + self.string_buffer.len() - string.len() - 1) as *const c_char
    }
}

enum VkMemberType {
    Var(*const c_char),
    Const(*const c_char),
    ConstPtr(*const c_char),
    MutPtr(*const c_char),
    ConstArray(*const c_char, usize),
    MutArray(*const c_char, usize),
    /// Default value to initialize with.
    Unknown
}

impl fmt::Debug for VkMemberType {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        use VkMemberType::*;
        if let Unknown = *self {
            write!(fmt, "Unknown")
        } else {
            let pt;
            let mut fmt_tuple = fmt.debug_tuple(
                match *self {
                    Var(p) => {pt = p; "Var"}
                    Const(p) => {pt = p; "Const"}
                    ConstPtr(p) => {pt = p; "ConstPtr"}
                    MutPtr(p) => {pt = p; "MutPtr"}
                    ConstArray(p, _) => {pt = p; "ConstArray"}
                    MutArray(p, _) => {pt = p; "MutArray"}
                    Unknown => unreachable!()
                });
            fmt_tuple.field(unsafe{ &if pt != ptr::null() {CStr::from_ptr(pt).to_str().unwrap()} else {"Error: Null Ptr"} });
            match *self {
                ConstArray(_, s) |
                MutArray(_, s) => fmt_tuple.field(&s),
                _ => &mut fmt_tuple
            }.finish()
        }
    }
}

struct VkMember {
    field_type: VkMemberType,
    field_name: *const c_char,
}

impl fmt::Debug for VkMember {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt .debug_struct("VkMember")
            .field("type", &self.field_type)
            .field("name", unsafe{ &CStr::from_ptr(self.field_name) })
            .finish()
    }
}

impl VkMember {
    fn empty() -> VkMember {
        VkMember {
            field_type: VkMemberType::Unknown,
            field_name: ptr::null()
        }
    }

    fn type_ptr(&self) -> Option<*const c_char> {
        use VkMemberType::*;
        match self.field_type {
            Var(s) |
            Const(s) |
            ConstPtr(s) |
            MutPtr(s) |
            ConstArray(s, _) |
            MutArray(s, _) => Some(s),
            Unknown => None
        }
    }

    fn set_type(&mut self, field_type: *const c_char) {
        use VkMemberType::*;
        match self.field_type {
            Var(ref mut s) |
            Const(ref mut s) |
            ConstPtr(ref mut s) |
            MutPtr(ref mut s) |
            ConstArray(ref mut s, _) |
            MutArray(ref mut s, _) =>
                if *s == ptr::null() {
                    *s = field_type
                } else {panic!("Field type already set")},
            Unknown => self.field_type = Var(field_type)

        }
    }

    fn change_type_var(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Unknown   => self.field_type = Var(ptr::null()),
            _ => panic!("Unexpected change type to var")
        }
    }

    fn change_type_const(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s) |
            Const(s) => self.field_type = Const(s),
            ConstPtr(_) |
            MutPtr(_) => panic!("Attempted changing mutability of pointer"),
            ConstArray(_, _) |
            MutArray(_, _) => panic!("Attempted changing mutability of array"),
            Unknown   => self.field_type = Const(ptr::null())
        }
    }

    fn change_type_ptr(&mut self) {
        use VkMemberType::*;
        match self.field_type {
            Var(s) => self.field_type = MutPtr(s),
            Const(s) => self.field_type = ConstPtr(s),
            MutPtr(_) |
            ConstPtr(_) => panic!("Attempted to change type from pointer to pointer"),
            ConstArray(_, _) |
            MutArray(_, _) => panic!("Attempted to change type from array to pointer"),
            Unknown   => self.field_type = MutPtr(ptr::null())
        }
    }

    fn change_type_array(&mut self, size: usize) -> &mut VkMember {
        use VkMemberType::*;
        match self.field_type {
            Var(s) => self.field_type = MutArray(s, size),
            Const(s) => self.field_type = ConstArray(s, size),
            MutPtr(_) |
            ConstPtr(_) => panic!("Attempted to change type from pointer to array"),
            ConstArray(_, _) |
            MutArray(_, _) => panic!("Attempted to change type from array to array"),
            Unknown => panic!("Attempted to change type to array without type identifier")
        }
        self
    }

    fn set_name(&mut self, field_name: *const c_char) {
        if self.field_name != ptr::null() {
            panic!("Unexpected \"name\" tag");
        } else {
            self.field_name = field_name;
        }
    }
}

/// A variant of a vulkan enum
#[derive(Debug)]
struct VkVariant {
    name: *const c_char,
    value: isize
}

#[derive(Debug)]
enum VkType {
    Struct {
        name: *const c_char,
        fields: Vec<VkMember>
    },

    Union {
        name: *const c_char,
        variants: Vec<VkMember>
    },

    Enum {
        name: *const c_char,
        variants: Vec<VkVariant>
    },

    BaseType {
        /// The type that is being aliased
        ty: *const c_char,
        /// The name of the new type
        name: *const c_char
    },

    Unhandled
}<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::types::TypeName;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use syn::{Ident, Type};

enum ArgumentConversionType {
    None,
    FromUniquePtrToValue,
    FromValueToUniquePtr,
}

pub(crate) struct ArgumentConversion {
    unwrapped_type: Type,
    conversion: ArgumentConversionType,
}

impl ArgumentConversion {
    pub(crate) fn new_unconverted(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::None,
        }
    }

    pub(crate) fn new_to_unique_ptr(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::FromValueToUniquePtr,
        }
    }

    pub(crate) fn new_from_unique_ptr(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::FromUniquePtrToValue,
        }
    }

    pub(crate) fn work_needed(&self) -> bool {
        !matches!(self.conversion, ArgumentConversionType::None)
    }

    fn unconverted_type(&self) -> String {
        match self.conversion {
            ArgumentConversionType::FromUniquePtrToValue => self.wrapped_type(),
            _ => self.unwrapped_type_as_string(),
        }
    }

    fn converted_type(&self) -> String {
        match self.conversion {
            ArgumentConversionType::FromValueToUniquePtr => self.wrapped_type(),
            _ => self.unwrapped_type_as_string(),
        }
    }

    fn unwrapped_type_as_string(&self) -> String {
        crate::types::to_cpp_name(&self.unwrapped_type)
    }

    fn wrapped_type(&self) -> String {
        format!("std::unique_ptr<{}>", self.unwrapped_type_as_string())
    }

    fn conversion(&self, var_name: &str) -> String {
        match self.conversion {
            ArgumentConversionType::None => var_name.to_string(),
            ArgumentConversionType::FromUniquePtrToValue => format!("std::move(*{})", var_name),
            ArgumentConversionType::FromValueToUniquePtr => format!(
                "std::make_unique<{}>({})",
                self.unconverted_type(),
                var_name
            ),
        }
    }
}

pub(crate) struct ByValueWrapper {
    pub(crate) id: Ident,
    pub(crate) return_conversion: Option<ArgumentConversion>,
    pub(crate) argument_conversion: Vec<ArgumentConversion>,
    pub(crate) extra_blocklist: Option<String>,
    pub(crate) is_a_method: bool,
}

/// Instructions for new C++ which we need to generate.
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    MakeUnique(TypeName, Vec<TypeName>),
    ByValueWrapper(ByValueWrapper),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    declaration: String,
    definition: String,
    name: String,
    suppress_older: Vec<String>,
    rename: Option<(String, String)>,
    headers: Vec<Header>,
}

/// Details of additional generated C++.
pub(crate) struct AdditionalCpp {
    pub(crate) declarations: String,
    pub(crate) definitions: String,
    pub(crate) extra_allowlist: Vec<String>,
    pub(crate) extra_blocklist: Vec<String>,
    pub(crate) renames: HashMap<String, String>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// At the moment, the only use here is for generating an ability
/// to do `make_unique` but more uses are expected in future.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct AdditionalCppGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl AdditionalCppGenerator {
    pub(crate) fn new(inclusions: String) -> Self {
        AdditionalCppGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    pub(crate) fn add_needs(&mut self, additions: Vec<AdditionalNeed>) {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::MakeUnique(ty, args) => self.generate_make_unique(&ty, &args),
                AdditionalNeed::ByValueWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(by_value_wrapper)
                }
            }
        }
    }

    pub(crate) fn generate(&self) -> Option<AdditionalCpp> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let declarations = self.concat_additional_items(|x| &x.declaration);
            let declarations = format!("{}\n{}\n{}", headers, self.inclusions, declarations);
            let definitions = self.concat_additional_items(|x| &x.definition);
            let definitions = format!("#include \"autocxxgen.h\"\n{}", definitions);
            let extra_allowlist = self
                .additional_functions
                .iter()
                .map(|x| x.name.to_string())
                .collect();
            let extra_blocklist = self
                .additional_functions
                .iter()
                .map(|x| x.suppress_older.clone())
                .flatten()
                .collect();
            let renames = self
                .additional_functions
                .iter()
                .filter_map(|x| x.rename.clone())
                .collect();
            Some(AdditionalCpp {
                declarations,
                definitions,
                extra_allowlist,
                extra_blocklist,
                renames,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> &str,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .collect::<Vec<&str>>()
            .join("\n\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let name = "make_string";
        let declaration = format!(
            "std::unique_ptr<std::string> {}(const ::rust::Str& str)",
            name
        );
        let definition = format!(
            "{} {{ return std::make_unique<std::string>(std::string(str)); }}",
            declaration
        );
        let declaration = format!("{};", declaration);

        self.additional_functions.push(AdditionalFunction {
            name: name.into(),
            declaration,
            definition,
            suppress_older: Vec::new(),
            rename: None,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_make_unique(&mut self, ty: &TypeName, constructor_arg_types: &[TypeName]) {
        let name = format!("{}_make_unique", ty.to_cpp_name());
        let constructor_args = constructor_arg_types
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} arg{}", ty.to_cpp_name(), counter))
            .join(", ");
        let declaration = format!("std::unique_ptr<{}> {}({})", ty, name, constructor_args);
        let arg_list = constructor_arg_types
            .iter()
            .enumerate()
            .map(|(counter, _)| format!("arg{}", counter))
            .join(", ");
        let definition = format!(
            "{} {{ return std::make_unique<{}>({}); }}",
            declaration, ty, arg_list
        );
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            name,
            declaration,
            definition,
            suppress_older: Vec::new(),
            rename: None,
            headers: vec![Header::system("memory")],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: ByValueWrapper) {
        let ident = details.id;
        let is_a_method = details.is_a_method;
        let name = format!("{}_up_wrapper", ident.to_string());
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} {}", ty.unconverted_type(), get_arg_name(counter)))
            .join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or("void".to_string(), |x| x.converted_type());
        let declaration = format!("{} {}({})", ret_type, name, args);
        let mut arg_list = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.conversion(&get_arg_name(counter)));
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = format!("{}({})", ident.to_string(), arg_list);
        if let Some(receiver) = receiver {
            underlying_function_call = format!("{}.{}", receiver, underlying_function_call);
        }
        if let Some(ret) = details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.conversion(&underlying_function_call));
        };
        let definition = format!("{} {{ {}; }}", declaration, underlying_function_call,);
        let declaration = format!("{};", declaration);
        let mut suppress_older = vec![ident.to_string()];
        suppress_older.extend(details.extra_blocklist.iter().cloned());
        self.additional_functions.push(AdditionalFunction {
            name: name.clone(),
            declaration,
            definition,
            suppress_older,
            rename: Some((name, ident.to_string())),
            headers: vec![Header::system("memory")],
        })
    }
}
<commit_msg>Fix make_string blooper<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::types::TypeName;
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use syn::{Ident, Type};

enum ArgumentConversionType {
    None,
    FromUniquePtrToValue,
    FromValueToUniquePtr,
}

pub(crate) struct ArgumentConversion {
    unwrapped_type: Type,
    conversion: ArgumentConversionType,
}

impl ArgumentConversion {
    pub(crate) fn new_unconverted(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::None,
        }
    }

    pub(crate) fn new_to_unique_ptr(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::FromValueToUniquePtr,
        }
    }

    pub(crate) fn new_from_unique_ptr(ty: Type) -> Self {
        ArgumentConversion {
            unwrapped_type: ty,
            conversion: ArgumentConversionType::FromUniquePtrToValue,
        }
    }

    pub(crate) fn work_needed(&self) -> bool {
        !matches!(self.conversion, ArgumentConversionType::None)
    }

    fn unconverted_type(&self) -> String {
        match self.conversion {
            ArgumentConversionType::FromUniquePtrToValue => self.wrapped_type(),
            _ => self.unwrapped_type_as_string(),
        }
    }

    fn converted_type(&self) -> String {
        match self.conversion {
            ArgumentConversionType::FromValueToUniquePtr => self.wrapped_type(),
            _ => self.unwrapped_type_as_string(),
        }
    }

    fn unwrapped_type_as_string(&self) -> String {
        crate::types::to_cpp_name(&self.unwrapped_type)
    }

    fn wrapped_type(&self) -> String {
        format!("std::unique_ptr<{}>", self.unwrapped_type_as_string())
    }

    fn conversion(&self, var_name: &str) -> String {
        match self.conversion {
            ArgumentConversionType::None => var_name.to_string(),
            ArgumentConversionType::FromUniquePtrToValue => format!("std::move(*{})", var_name),
            ArgumentConversionType::FromValueToUniquePtr => format!(
                "std::make_unique<{}>({})",
                self.unconverted_type(),
                var_name
            ),
        }
    }
}

pub(crate) struct ByValueWrapper {
    pub(crate) id: Ident,
    pub(crate) return_conversion: Option<ArgumentConversion>,
    pub(crate) argument_conversion: Vec<ArgumentConversion>,
    pub(crate) extra_blocklist: Option<String>,
    pub(crate) is_a_method: bool,
}

/// Instructions for new C++ which we need to generate.
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    MakeUnique(TypeName, Vec<TypeName>),
    ByValueWrapper(ByValueWrapper),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    declaration: String,
    definition: String,
    name: String,
    suppress_older: Vec<String>,
    rename: Option<(String, String)>,
    headers: Vec<Header>,
}

/// Details of additional generated C++.
pub(crate) struct AdditionalCpp {
    pub(crate) declarations: String,
    pub(crate) definitions: String,
    pub(crate) extra_allowlist: Vec<String>,
    pub(crate) extra_blocklist: Vec<String>,
    pub(crate) renames: HashMap<String, String>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// At the moment, the only use here is for generating an ability
/// to do `make_unique` but more uses are expected in future.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct AdditionalCppGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl AdditionalCppGenerator {
    pub(crate) fn new(inclusions: String) -> Self {
        AdditionalCppGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    pub(crate) fn add_needs(&mut self, additions: Vec<AdditionalNeed>) {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::MakeUnique(ty, args) => self.generate_make_unique(&ty, &args),
                AdditionalNeed::ByValueWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(by_value_wrapper)
                }
            }
        }
    }

    pub(crate) fn generate(&self) -> Option<AdditionalCpp> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let declarations = self.concat_additional_items(|x| &x.declaration);
            let declarations = format!("{}\n{}\n{}", headers, self.inclusions, declarations);
            let definitions = self.concat_additional_items(|x| &x.definition);
            let definitions = format!("#include \"autocxxgen.h\"\n{}", definitions);
            let extra_allowlist = self
                .additional_functions
                .iter()
                .map(|x| x.name.to_string())
                .collect();
            let extra_blocklist = self
                .additional_functions
                .iter()
                .map(|x| x.suppress_older.clone())
                .flatten()
                .collect();
            let renames = self
                .additional_functions
                .iter()
                .filter_map(|x| x.rename.clone())
                .collect();
            Some(AdditionalCpp {
                declarations,
                definitions,
                extra_allowlist,
                extra_blocklist,
                renames,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> &str,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .collect::<Vec<&str>>()
            .join("\n\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let name = "make_string";
        let declaration = format!(
            "std::unique_ptr<std::string> {}(::rust::Str str)",
            name
        );
        let definition = format!(
            "{} {{ return std::make_unique<std::string>(std::string(str)); }}",
            declaration
        );
        let declaration = format!("{};", declaration);

        self.additional_functions.push(AdditionalFunction {
            name: name.into(),
            declaration,
            definition,
            suppress_older: Vec::new(),
            rename: None,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_make_unique(&mut self, ty: &TypeName, constructor_arg_types: &[TypeName]) {
        let name = format!("{}_make_unique", ty.to_cpp_name());
        let constructor_args = constructor_arg_types
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} arg{}", ty.to_cpp_name(), counter))
            .join(", ");
        let declaration = format!("std::unique_ptr<{}> {}({})", ty, name, constructor_args);
        let arg_list = constructor_arg_types
            .iter()
            .enumerate()
            .map(|(counter, _)| format!("arg{}", counter))
            .join(", ");
        let definition = format!(
            "{} {{ return std::make_unique<{}>({}); }}",
            declaration, ty, arg_list
        );
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            name,
            declaration,
            definition,
            suppress_older: Vec::new(),
            rename: None,
            headers: vec![Header::system("memory")],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: ByValueWrapper) {
        let ident = details.id;
        let is_a_method = details.is_a_method;
        let name = format!("{}_up_wrapper", ident.to_string());
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} {}", ty.unconverted_type(), get_arg_name(counter)))
            .join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or("void".to_string(), |x| x.converted_type());
        let declaration = format!("{} {}({})", ret_type, name, args);
        let mut arg_list = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.conversion(&get_arg_name(counter)));
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = format!("{}({})", ident.to_string(), arg_list);
        if let Some(receiver) = receiver {
            underlying_function_call = format!("{}.{}", receiver, underlying_function_call);
        }
        if let Some(ret) = details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.conversion(&underlying_function_call));
        };
        let definition = format!("{} {{ {}; }}", declaration, underlying_function_call,);
        let declaration = format!("{};", declaration);
        let mut suppress_older = vec![ident.to_string()];
        suppress_older.extend(details.extra_blocklist.iter().cloned());
        self.additional_functions.push(AdditionalFunction {
            name: name.clone(),
            declaration,
            definition,
            suppress_older,
            rename: Some((name, ident.to_string())),
            headers: vec![Header::system("memory")],
        })
    }
}
<|endoftext|>


<commit_before>use std::io;
use byteorder::WriteBytesExt;
use super::{MessageType, ReadFrom, ReadError, WriteTo};
use super::messages::{AddEntry, EntryAdded, EntryExpired, RemoveEntry, SetWatchMode, ErrorMessage};

#[derive(Debug)]
pub enum Message {
    Ping,
    Pong,
    AddEntry(AddEntry),
    EntryAdded(EntryAdded),
    RemoveEntry(RemoveEntry),
    // `EntryRemoved` should also contain the entry's data. However, this requires changing
    // libradium, because the frontend does not block when adding or removing entries.
    EntryRemoved,
    EntryExpired(EntryExpired),
    SetWatchMode(SetWatchMode),
    Ok,
    // TODO: Error message must contain an error value
    Error(ErrorMessage),
}

impl Message {
    pub fn message_type(&self) -> MessageType {
        match self {
            &Message::Ping => MessageType::Ping,
            &Message::Pong => MessageType::Pong,
            &Message::AddEntry(..) => MessageType::AddEntry,
            &Message::EntryAdded(..) => MessageType::EntryAdded,
            &Message::RemoveEntry(..) => MessageType::RemoveEntry,
            &Message::EntryRemoved => MessageType::EntryRemoved,
            &Message::EntryExpired(..) => MessageType::EntryExpired,
            &Message::SetWatchMode(..) => MessageType::SetWatchMode,
            &Message::Ok => MessageType::Ok,
            &Message::Error(..) => MessageType::Error,
        }
    }

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

impl ReadFrom for Message {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let msg_type = MessageType::read_from(source)?;

        match msg_type {
            MessageType::Ping => Ok(Message::Ping),
            MessageType::Pong => Ok(Message::Pong),
            MessageType::AddEntry => Ok(Message::AddEntry(AddEntry::read_from(source)?)),
            MessageType::EntryAdded => Ok(Message::EntryAdded(EntryAdded::read_from(source)?)),
            MessageType::RemoveEntry => Ok(Message::RemoveEntry(RemoveEntry::read_from(source)?)),
            MessageType::EntryRemoved => Ok(Message::EntryRemoved),
            MessageType::EntryExpired => Ok(Message::EntryExpired(EntryExpired::read_from(source)?)),
            MessageType::SetWatchMode => Ok(Message::SetWatchMode(SetWatchMode::read_from(source)?)),
            MessageType::Ok => Ok(Message::Ok),
            MessageType::Error => Ok(Message::Error(ErrorMessage::read_from(source)?)),
        }
    }
}

impl WriteTo for Message {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.message_type().into())?;

        match self {
            &Message::Ping => Ok(()),
            &Message::Pong => Ok(()),
            &Message::AddEntry(ref msg) => msg.write_to(target),
            &Message::EntryAdded(ref msg) => msg.write_to(target),
            &Message::RemoveEntry(ref msg) => msg.write_to(target),
            &Message::EntryRemoved => Ok(()),
            &Message::EntryExpired(ref msg) => msg.write_to(target),
            &Message::SetWatchMode(ref msg) => msg.write_to(target),
            &Message::Ok => Ok(()),
            &Message::Error(ref msg) => msg.write_to(target),
        }
    }
}

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

    macro_rules! test_message {
        ($test:ident, $ty:ident) => {
            test_message!($test, Message::$ty, MessageType::$ty);
        };
        ($test:ident, $msg:expr, $ty:expr) => {
            #[test]
            fn $test() {
                let msg = $msg;

                assert_eq!($ty, msg.message_type());

                let mut vec = Vec::new();
                assert!(msg.write_to(&mut vec).is_ok());
                assert_eq!(msg.message_type().to_u8(), vec[0]);

                let read_msg = Message::read_from(&mut vec.as_mut_slice().as_ref()).unwrap();
                assert_eq!(msg.message_type(), read_msg.message_type());
            }
        };
    }

    test_message!(test_ping, Ping);
    test_message!(test_pong, Pong);

    test_message!(test_add_entry,
                  Message::AddEntry(AddEntry::new(0, vec![])),
                  MessageType::AddEntry);

    test_message!(test_entry_added,
                  Message::EntryAdded(EntryAdded::new(0, 0)),
                  MessageType::EntryAdded);

    test_message!(test_remove_entry,
                  Message::RemoveEntry(RemoveEntry::new(0, 0)),
                  MessageType::RemoveEntry);

    test_message!(test_entry_removed, EntryRemoved);

    test_message!(test_entry_expired,
                  Message::EntryExpired(EntryExpired::new(0, 7, vec![])),
                  MessageType::EntryExpired);

    test_message!(test_ok, Ok);

    test_message!(test_set_watch_mode,
                  Message::SetWatchMode(SetWatchMode::new(WatchMode::None)),
                  MessageType::SetWatchMode);

    test_message!(test_error, Message::Error(ErrorMessage::new(ErrorCode::ClientRejected)), MessageType::Error);
}<commit_msg>Remove todo<commit_after>use std::io;
use byteorder::WriteBytesExt;
use super::{MessageType, ReadFrom, ReadError, WriteTo};
use super::messages::{AddEntry, EntryAdded, EntryExpired, RemoveEntry, SetWatchMode, ErrorMessage};

#[derive(Debug)]
pub enum Message {
    Ping,
    Pong,
    AddEntry(AddEntry),
    EntryAdded(EntryAdded),
    RemoveEntry(RemoveEntry),
    // `EntryRemoved` should also contain the entry's data. However, this requires changing
    // libradium, because the frontend does not block when adding or removing entries.
    EntryRemoved,
    EntryExpired(EntryExpired),
    SetWatchMode(SetWatchMode),
    Ok,
    Error(ErrorMessage),
}

impl Message {
    pub fn message_type(&self) -> MessageType {
        match self {
            &Message::Ping => MessageType::Ping,
            &Message::Pong => MessageType::Pong,
            &Message::AddEntry(..) => MessageType::AddEntry,
            &Message::EntryAdded(..) => MessageType::EntryAdded,
            &Message::RemoveEntry(..) => MessageType::RemoveEntry,
            &Message::EntryRemoved => MessageType::EntryRemoved,
            &Message::EntryExpired(..) => MessageType::EntryExpired,
            &Message::SetWatchMode(..) => MessageType::SetWatchMode,
            &Message::Ok => MessageType::Ok,
            &Message::Error(..) => MessageType::Error,
        }
    }

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

impl ReadFrom for Message {
    fn read_from<R: io::Read>(source: &mut R) -> Result<Self, ReadError> {
        let msg_type = MessageType::read_from(source)?;

        match msg_type {
            MessageType::Ping => Ok(Message::Ping),
            MessageType::Pong => Ok(Message::Pong),
            MessageType::AddEntry => Ok(Message::AddEntry(AddEntry::read_from(source)?)),
            MessageType::EntryAdded => Ok(Message::EntryAdded(EntryAdded::read_from(source)?)),
            MessageType::RemoveEntry => Ok(Message::RemoveEntry(RemoveEntry::read_from(source)?)),
            MessageType::EntryRemoved => Ok(Message::EntryRemoved),
            MessageType::EntryExpired => Ok(Message::EntryExpired(EntryExpired::read_from(source)?)),
            MessageType::SetWatchMode => Ok(Message::SetWatchMode(SetWatchMode::read_from(source)?)),
            MessageType::Ok => Ok(Message::Ok),
            MessageType::Error => Ok(Message::Error(ErrorMessage::read_from(source)?)),
        }
    }
}

impl WriteTo for Message {
    fn write_to<W: io::Write>(&self, target: &mut W) -> io::Result<()> {
        target.write_u8(self.message_type().into())?;

        match self {
            &Message::Ping => Ok(()),
            &Message::Pong => Ok(()),
            &Message::AddEntry(ref msg) => msg.write_to(target),
            &Message::EntryAdded(ref msg) => msg.write_to(target),
            &Message::RemoveEntry(ref msg) => msg.write_to(target),
            &Message::EntryRemoved => Ok(()),
            &Message::EntryExpired(ref msg) => msg.write_to(target),
            &Message::SetWatchMode(ref msg) => msg.write_to(target),
            &Message::Ok => Ok(()),
            &Message::Error(ref msg) => msg.write_to(target),
        }
    }
}

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

    macro_rules! test_message {
        ($test:ident, $ty:ident) => {
            test_message!($test, Message::$ty, MessageType::$ty);
        };
        ($test:ident, $msg:expr, $ty:expr) => {
            #[test]
            fn $test() {
                let msg = $msg;

                assert_eq!($ty, msg.message_type());

                let mut vec = Vec::new();
                assert!(msg.write_to(&mut vec).is_ok());
                assert_eq!(msg.message_type().to_u8(), vec[0]);

                let read_msg = Message::read_from(&mut vec.as_mut_slice().as_ref()).unwrap();
                assert_eq!(msg.message_type(), read_msg.message_type());
            }
        };
    }

    test_message!(test_ping, Ping);
    test_message!(test_pong, Pong);

    test_message!(test_add_entry,
                  Message::AddEntry(AddEntry::new(0, vec![])),
                  MessageType::AddEntry);

    test_message!(test_entry_added,
                  Message::EntryAdded(EntryAdded::new(0, 0)),
                  MessageType::EntryAdded);

    test_message!(test_remove_entry,
                  Message::RemoveEntry(RemoveEntry::new(0, 0)),
                  MessageType::RemoveEntry);

    test_message!(test_entry_removed, EntryRemoved);

    test_message!(test_entry_expired,
                  Message::EntryExpired(EntryExpired::new(0, 7, vec![])),
                  MessageType::EntryExpired);

    test_message!(test_ok, Ok);

    test_message!(test_set_watch_mode,
                  Message::SetWatchMode(SetWatchMode::new(WatchMode::None)),
                  MessageType::SetWatchMode);

    test_message!(test_error, Message::Error(ErrorMessage::new(ErrorCode::ClientRejected)), MessageType::Error);
}<|endoftext|>


<commit_before>use std::iter::successors;

use hir::db::HirDatabase;
use ra_syntax::{ast, AstNode, TextUnit, T};

use crate::{Assist, AssistCtx, AssistId};

pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let colon_colon = ctx.token_at_offset().find(|leaf| leaf.kind() == T![::])?;
    let path = ast::Path::cast(colon_colon.parent())?;
    let top_path = successors(Some(path), |it| it.parent_path()).last()?;

    let _use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast)?;

    let l_curly = colon_colon.range().end();
    let r_curly = match top_path.syntax().parent().and_then(ast::UseTree::cast) {
        Some(tree) => tree.syntax().range().end(),
        None => top_path.syntax().range().end(),
    };

    ctx.add_action(AssistId("split_import"), "split import", |edit| {
        edit.target(colon_colon.range());
        edit.insert(l_curly, "{");
        edit.insert(r_curly, "}");
        edit.set_cursor(l_curly + TextUnit::of_str("{"));
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_target};

    #[test]
    fn test_split_import() {
        check_assist(
            split_import,
            "use crate::<|>db::RootDatabase;",
            "use crate::{<|>db::RootDatabase};",
        )
    }

    #[test]
    fn split_import_works_with_trees() {
        check_assist(
            split_import,
            "use algo:<|>:visitor::{Visitor, visit}",
            "use algo::{<|>visitor::{Visitor, visit}}",
        )
    }

    #[test]
    fn split_import_target() {
        check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::");
    }
}
<commit_msg>readability<commit_after>use std::iter::successors;

use hir::db::HirDatabase;
use ra_syntax::{ast, AstNode, TextUnit, T};

use crate::{Assist, AssistCtx, AssistId};

pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let colon_colon = ctx.token_at_offset().find(|leaf| leaf.kind() == T![::])?;
    let path = ast::Path::cast(colon_colon.parent())?;
    let top_path = successors(Some(path), |it| it.parent_path()).last()?;

    let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast);
    if use_tree.is_none() {
        return None;
    }

    let l_curly = colon_colon.range().end();
    let r_curly = match top_path.syntax().parent().and_then(ast::UseTree::cast) {
        Some(tree) => tree.syntax().range().end(),
        None => top_path.syntax().range().end(),
    };

    ctx.add_action(AssistId("split_import"), "split import", |edit| {
        edit.target(colon_colon.range());
        edit.insert(l_curly, "{");
        edit.insert(r_curly, "}");
        edit.set_cursor(l_curly + TextUnit::of_str("{"));
    });

    ctx.build()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_target};

    #[test]
    fn test_split_import() {
        check_assist(
            split_import,
            "use crate::<|>db::RootDatabase;",
            "use crate::{<|>db::RootDatabase};",
        )
    }

    #[test]
    fn split_import_works_with_trees() {
        check_assist(
            split_import,
            "use algo:<|>:visitor::{Visitor, visit}",
            "use algo::{<|>visitor::{Visitor, visit}}",
        )
    }

    #[test]
    fn split_import_target() {
        check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::");
    }
}
<|endoftext|>


<commit_before>use errors::{Error, Result};
use stringutils::status_to_string;

/// Error type
///
/// `cfitsio` passes errors through integer status codes. This struct wraps this and its associated
/// error message.
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    pub status: i32,
    pub message: String,
}

/// Function for chaining result types
pub fn check_status(status: i32) -> Result<()> {
    match status {
        0 => Ok(()),
        _ => Err(Error::Fits(FitsError {
            status: status,
            message: status_to_string(status).unwrap().unwrap(),
        })),
    }
}

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

    #[test]
    fn test_check_status_ok() {
        assert_eq!(check_status(0), Ok(()));
    }

    #[test]
    fn test_check_status_ok_with_value() {
        assert_eq!(check_status(0).map(|_| 10i32), Ok(10i32));
    }

    #[test]
    fn test_check_status_with_err() {
        assert!(check_status(105).map(|_| 10i32).is_err());
    }
}
<commit_msg>Simplify error handling<commit_after>use errors::{Error, Result};
use stringutils::status_to_string;

/// Error type
///
/// `cfitsio` passes errors through integer status codes. This struct wraps this and its associated
/// error message.
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    pub status: i32,
    pub message: String,
}

/// Function for chaining result types
pub fn check_status(status: i32) -> Result<()> {
    match status {
        0 => Ok(()),
        _ => Err(Error::Fits(FitsError {
            status: status,
            message: status_to_string(status)?.expect("guaranteed to be Some"),
        })),
    }
}

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

    #[test]
    fn test_check_status_ok() {
        assert_eq!(check_status(0), Ok(()));
    }

    #[test]
    fn test_check_status_ok_with_value() {
        assert_eq!(check_status(0).map(|_| 10i32), Ok(10i32));
    }

    #[test]
    fn test_check_status_with_err() {
        assert!(check_status(105).map(|_| 10i32).is_err());
    }
}
<|endoftext|>


<commit_before>use crate::lexical::LexerError;
use crate::span::Span;
use crate::syntax::{ParserError, ParserErrorKind};

use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Write;
use std::str;

// TODO: Use termion or something for this
const RED_FG: &str = "\x1B[38;5;1m";
const GREEN_FG: &str = "\x1B[38;5;2m";
const YELLOW_FG: &str = "\x1B[38;5;3m";
const BLUE_FG: &str = "\x1B[38;5;4m";
const MAGENTA_FG: &str = "\x1B[38;5;5m";
const LIGHT_BLUE_FG: &str = "\x1B[38;5;6m";
const DARK_GRAY_FG: &str = "\x1B[38;5;8m";
const LIGHT_RED_FG: &str = "\x1B[38;5;9m";
const WHITE_FG: &str = "\x1B[38;5;15m";
const CLEAR: &str = "\x1B[0m";

// REVIEW: Maybe name "ContextualizedError"?
#[derive(Debug)]
pub struct UserfacingError<'intern, 's> {
    pub file_name: &'intern str,
    pub file_str: &'s str,
    pub error: ParserError<'s>,
}

impl<'s> UserfacingError<'_, 's> {
    // Returns the full source line and a Span adjusted to the substr.
    fn get_line(&self, span: Span) -> (&'s str, u32, Span) {
        let file_bytes = self.file_str.as_bytes();
        let Span { mut start_idx, mut end_idx, file_id } = span;
        let mut line_no = 1;

        while end_idx < file_bytes.len() && file_bytes[end_idx + 1] != '\n' as u8 {
            end_idx += 1;
        }

        while start_idx > 0 && file_bytes[start_idx - 1] != '\n' as u8 {
            start_idx -= 1;
        }

        // TODO: Maybe we can cache line info/location so that the entire source isn't scoured
        // multiple times.
        for ch in self.file_str[..=start_idx].chars() {
            if ch == '\n' {
                line_no += 1;
            }
        }

        let adjusted_span = Span::new(file_id, span.start_idx - start_idx, end_idx - start_idx);

        (&self.file_str[start_idx..=end_idx], line_no, adjusted_span)
    }
}

impl Display for UserfacingError<'_, '_> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        let (err_msg, span) = match self.error.kind {
            ParserErrorKind::LexerError(le) => match le {
                LexerError::IncompleteNumeric(num) => {
                    if num.node() == "0b" {
                        ("Found an incomplete binary literal", num.span())
                    } else if num.node() == "0x" {
                        ("Found an incomplete hex literal", num.span())
                    } else {
                        unreachable!()
                    }
                },
                LexerError::InvalidNumeric(num, suffix) => {
                    if num.node() == "0b" {
                        ("Found an invalid binary literal", suffix.span())
                    } else if num.node() == "0x" {
                        ("Found an invalid hex literal", suffix.span())
                    } else {
                        unreachable!()
                    }
                },
                _ => todo!(),
            },
            ParserErrorKind::UnexpectedToken(tok) => todo!(),
        };

        let (line, line_no, adjusted_span) = self.get_line(span);
        let mut line_no_str = [0u8; 10];
        let mut column_no_str = [0u8; 10];

        write!(&mut line_no_str as &mut [u8], "{}", line_no).expect("u32 to always fit in 10 bytes");
        write!(&mut column_no_str as &mut [u8], "{}", adjusted_span.start_idx + 1).expect("u32 to always fit in 10 bytes");

        let line_no_str = str::from_utf8(&line_no_str[..bytes_written(&line_no_str)]).unwrap();
        let column_no_str = str::from_utf8(&column_no_str[..bytes_written(&column_no_str)]).unwrap();
        let n_spaces = line_no_str.len() + 1;

        // TODO: Split this into methods?
        f.write_str(RED_FG)?;
        f.write_str("⚑ error")?;
        f.write_str(WHITE_FG)?;
        f.write_str(": ")?;
        f.write_str(err_msg)?;
        f.write_str(BLUE_FG)?;

        write_nl_n_spaces(f, n_spaces)?;

        f.write_str("┏━━▶ ")?;
        f.write_str(CLEAR)?;
        f.write_str(self.file_name)?;
        f.write_str(":")?;
        f.write_str(line_no_str)?;
        f.write_str(":")?;
        f.write_str(column_no_str)?;
        f.write_str(BLUE_FG)?;
        f.write_str("\n")?;
        f.write_str(line_no_str)?;
        f.write_str(" ┃")?;

        write_nl_n_spaces(f, n_spaces)?;

        f.write_str("┗━━▶ ")?;
        f.write_str(CLEAR)?;
        f.write_str(line)?;
        f.write_str(RED_FG)?;

        write_nl_n_spaces(f, n_spaces + adjusted_span.start_idx + 5)?;
        write_n_chars(f, adjusted_span.width(), "^")?;

        f.write_str(CLEAR)?;
        f.write_str("\n")
    }
}

fn bytes_written(bytes: &[u8]) -> usize {
    bytes.iter()
        .position(|&byte| byte == 0)
        .unwrap_or(bytes.len())
}

#[inline]
fn write_n_chars(f: &mut Formatter, n: usize, s: &str) -> FmtResult {
    for _ in 0..n {
        f.write_str(s)?;
    }

    Ok(())
}

#[inline]
fn write_nl_n_spaces(f: &mut Formatter, n: usize) -> FmtResult {
    f.write_str("\n")?;

    write_n_chars(f, n, " ")
}
<commit_msg>Fixed span end bug<commit_after>use crate::lexical::LexerError;
use crate::span::Span;
use crate::syntax::{ParserError, ParserErrorKind};

use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Write;
use std::str;

// TODO: Use termion or something for this
const RED_FG: &str = "\x1B[38;5;1m";
const GREEN_FG: &str = "\x1B[38;5;2m";
const YELLOW_FG: &str = "\x1B[38;5;3m";
const BLUE_FG: &str = "\x1B[38;5;4m";
const MAGENTA_FG: &str = "\x1B[38;5;5m";
const LIGHT_BLUE_FG: &str = "\x1B[38;5;6m";
const DARK_GRAY_FG: &str = "\x1B[38;5;8m";
const LIGHT_RED_FG: &str = "\x1B[38;5;9m";
const WHITE_FG: &str = "\x1B[38;5;15m";
const CLEAR: &str = "\x1B[0m";

// REVIEW: Maybe name "ContextualizedError"?
#[derive(Debug)]
pub struct UserfacingError<'intern, 's> {
    pub file_name: &'intern str,
    pub file_str: &'s str,
    pub error: ParserError<'s>,
}

impl<'s> UserfacingError<'_, 's> {
    // Returns the full source line and a Span adjusted to the substr.
    fn get_line(&self, span: Span) -> (&'s str, u32, Span) {
        let file_bytes = self.file_str.as_bytes();
        let Span { mut start_idx, mut end_idx, file_id } = span;
        let mut line_no = 1;

        while end_idx < file_bytes.len() && file_bytes[end_idx + 1] != '\n' as u8 {
            end_idx += 1;
        }

        while start_idx > 0 && file_bytes[start_idx - 1] != '\n' as u8 {
            start_idx -= 1;
        }

        // TODO: Maybe we can cache line info/location so that the entire source isn't scoured
        // multiple times.
        for ch in self.file_str[..=start_idx].chars() {
            if ch == '\n' {
                line_no += 1;
            }
        }

        let adjusted_span = Span::new(file_id, span.start_idx - start_idx, span.end_idx - start_idx);

        (&self.file_str[start_idx..=end_idx], line_no, adjusted_span)
    }
}

impl Display for UserfacingError<'_, '_> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        let (err_msg, span) = match self.error.kind {
            ParserErrorKind::LexerError(le) => match le {
                LexerError::IncompleteNumeric(num) => {
                    if num.node() == "0b" {
                        ("Found an incomplete binary literal", num.span())
                    } else if num.node() == "0x" {
                        ("Found an incomplete hex literal", num.span())
                    } else {
                        unreachable!()
                    }
                },
                LexerError::InvalidNumeric(num, suffix) => {
                    if num.node() == "0b" {
                        ("Found an invalid binary literal", suffix.span())
                    } else if num.node() == "0x" {
                        ("Found an invalid hex literal", suffix.span())
                    } else {
                        unreachable!()
                    }
                },
                _ => todo!(),
            },
            ParserErrorKind::UnexpectedToken(tok) => todo!(),
        };

        let (line, line_no, adjusted_span) = self.get_line(span);
        let mut line_no_str = [0u8; 10];
        let mut column_no_str = [0u8; 10];

        write!(&mut line_no_str as &mut [u8], "{}", line_no).expect("u32 to always fit in 10 bytes");
        write!(&mut column_no_str as &mut [u8], "{}", adjusted_span.start_idx + 1).expect("u32 to always fit in 10 bytes");

        let line_no_str = str::from_utf8(&line_no_str[..bytes_written(&line_no_str)]).unwrap();
        let column_no_str = str::from_utf8(&column_no_str[..bytes_written(&column_no_str)]).unwrap();
        let n_spaces = line_no_str.len() + 1;

        // TODO: Split this into methods?
        f.write_str(RED_FG)?;
        f.write_str("⚑ error")?;
        f.write_str(WHITE_FG)?;
        f.write_str(": ")?;
        f.write_str(err_msg)?;
        f.write_str(BLUE_FG)?;

        write_nl_n_spaces(f, n_spaces)?;

        f.write_str("┏━━▶ ")?;
        f.write_str(CLEAR)?;
        f.write_str(self.file_name)?;
        f.write_str(":")?;
        f.write_str(line_no_str)?;
        f.write_str(":")?;
        f.write_str(column_no_str)?;
        f.write_str(BLUE_FG)?;
        f.write_str("\n")?;
        f.write_str(line_no_str)?;
        f.write_str(" ┃")?;

        write_nl_n_spaces(f, n_spaces)?;

        f.write_str("┗━━▶ ")?;
        f.write_str(CLEAR)?;
        f.write_str(line)?;
        f.write_str(RED_FG)?;

        write_nl_n_spaces(f, n_spaces + adjusted_span.start_idx + 5)?;
        write_n_chars(f, adjusted_span.width(), "^")?;

        f.write_str(CLEAR)?;
        f.write_str("\n")
    }
}

fn bytes_written(bytes: &[u8]) -> usize {
    bytes.iter()
        .position(|&byte| byte == 0)
        .unwrap_or(bytes.len())
}

#[inline]
fn write_n_chars(f: &mut Formatter, n: usize, s: &str) -> FmtResult {
    for _ in 0..n {
        f.write_str(s)?;
    }

    Ok(())
}

#[inline]
fn write_nl_n_spaces(f: &mut Formatter, n: usize) -> FmtResult {
    f.write_str("\n")?;

    write_n_chars(f, n, " ")
}
<|endoftext|>


<commit_before>use ::front::ast;

pub fn check_target(functions: &ast::Functions, fun: &ast::Node<ast::Function>) {
    fun.node.body.visit_expr(&mut |expr| {
        match expr.node {
            ast::Expression::Call { ref function, .. } => assert!(functions.contains_key(function)),
            _ => (),
        };
        true
    });
}
<commit_msg>Front: Add CallsUnknownFunction error<commit_after>use std::error::Error;
use std::fmt;
use std::result;

use ::front::ast;

pub type Result<'a> = result::Result<(), CallsUnknownFunction<'a>>;

pub fn check_target<'a>(functions: &'a ast::Functions,
                        fun: &'a ast::Node<ast::Function>)
                        -> Result<'a> {
    let mut res = Ok(());

    fun.node.body.visit_expr(&mut |expr| {
        match expr.node {
            ast::Expression::Call { ref function, .. } if !functions.contains_key(function) => {
                res = Err(CallsUnknownFunction { call: expr });
                false
            }
            _ => true,
        }
    });

    res
}

#[derive(PartialEq, Debug)]
pub struct CallsUnknownFunction<'a> {
    call: &'a ast::Node<ast::Expression>,
}

impl<'a> fmt::Display for CallsUnknownFunction<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.call.node {
            ast::Expression::Call { ref function, .. } => {
                write!(f,
                       "{}.{} CallsUnknownFunction: `{}`",
                       self.call.pos.line,
                       self.call.pos.col,
                       function)
            }
            _ => panic!("expression not an Expression::Call"),
        }
    }
}

impl<'a> Error for CallsUnknownFunction<'a> {
    fn description(&self) -> &str {
        "call to unknown function"
    }
}
<|endoftext|>


<commit_before>//! Traits giving structural informations on linear algebra objects or the space they live in.

use std::num::Zero;
use std::vec::{VecIterator, VecMutIterator};
use traits::operations::{RMul, LMul, ScalarAdd, ScalarSub};
use traits::geometry::{Dot, Norm, UniformSphereSample};

/// Trait of matrices.
///
/// A matrix has rows and columns and are able to multiply them.
pub trait Mat<R, C> : Row<R> + Col<C> + RMul<R> + LMul<C> { }

impl<M: Row<R> + Col<C> + RMul<R> + LMul<C>, R, C> Mat<R, C> for M {
}

/// Trait of matrices which can be converted to another matrix.
///
/// Use this to change easily the type of a matrix components.
pub trait MatCast<M> {
    /// Converts `m` to have the type `M`.
    fn from(m: Self) -> M;
}

// XXX: we keep ScalarAdd and ScalarSub here to avoid trait impl conflict (overriding) between the
// different Add/Sub traits. This is _so_ unfortunate…

// NOTE: cant call that `Vector` because it conflicts with std::Vector
/// Trait grouping most common operations on vectors.
pub trait Vec<N>: Dim + Sub<Self, Self> + Add<Self, Self> + Neg<Self> + Zero + Eq + Mul<N, Self>
                  + Div<N, Self> + Dot<N> {
}

/// Trait of vector with components implementing the `Algebraic` trait.
pub trait AlgebraicVec<N: Algebraic>: Vec<N> + Norm<N> {
}

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait VecExt<N>: Vec<N> + Basis + Indexable<uint, N> + Iterable<N> + Round +
                     UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable
{ }

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait AlgebraicVecExt<N: Algebraic>: AlgebraicVec<N> + VecExt<N> { }

impl<N, V: Dim + Sub<V, V> + Add<V, V> + Neg<V> + Zero + Eq + Mul<N, V> + Div<N, V> + Dot<N>>
Vec<N> for V { }

impl<N: Algebraic, V: Vec<N> + Norm<N>> AlgebraicVec<N> for V { }

impl<N,
     V: Vec<N> + Basis + Indexable<uint, N> + Iterable<N> + Round +
        UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable>
VecExt<N> for V { }

impl<N: Algebraic, V: AlgebraicVec<N> + VecExt<N>> AlgebraicVecExt<N> for V { }

/// Trait of vectors which can be converted to another vector. Used to change the type of a vector
/// components.
pub trait VecCast<V> {
    /// Converts `v` to have the type `V`.
    fn from(v: Self) -> V;
}

// FIXME: return an iterator instead
/// Traits of objects which can form a basis (typically vectors).
pub trait Basis {
    /// Iterates through the canonical basis of the space in which this object lives.
    fn canonical_basis(&fn(Self) -> bool);

    /// Iterates through a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis(&self, &fn(Self) -> bool);

    /// Creates the canonical basis of the space in which this object lives.
    fn canonical_basis_list() -> ~[Self] {
        let mut res = ~[];

        do Basis::canonical_basis |elem| {
            res.push(elem);

            true
        }

        res
    }

    /// Creates a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis_list(&self) -> ~[Self] {
        let mut res = ~[];

        do self.orthonormal_subspace_basis |elem| {
            res.push(elem);

            true
        }

        res
    }
}

/// Trait to access rows of a matrix or a vector.
pub trait Row<R> {
    /// The number of column of `self`.
    fn num_rows(&self) -> uint;
    /// Reads the `i`-th row of `self`.
    fn row(&self, i: uint) -> R;
    /// Writes the `i`-th row of `self`.
    fn set_row(&mut self, i: uint, R);

    // FIXME: add iterators on rows: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait to access columns of a matrix or vector.
pub trait Col<C> {
    /// The number of column of this matrix or vector.
    fn num_cols(&self) -> uint;

    /// Reads the `i`-th column of `self`.
    fn col(&self, i: uint) -> C;

    /// Writes the `i`-th column of `self`.
    fn set_col(&mut self, i: uint, C);

    // FIXME: add iterators on columns: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait of objects having a spacial dimension known at compile time.
pub trait Dim {
    /// The dimension of the object.
    fn dim(unused_self: Option<Self>) -> uint;
}

// FIXME: this trait should not be on nalgebra.
// however, it is needed because std::ops::Index is (strangely) to poor: it
// does not have a function to set values.
// Also, using Index with tuples crashes.
/// This is a workaround of current Rust limitations.
///
/// It exists because the `Index` trait cannot be used to express write access.
/// Thus, this is the same as the `Index` trait but without the syntactic sugar and with a method
/// to write to a specific index.
pub trait Indexable<Index, Res> {
    /// Reads the `i`-th element of `self`.
    fn at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    fn set(&mut self, i: Index, Res);
    /// Swaps the `i`-th element of `self` with its `j`-th element.
    fn swap(&mut self, i: Index, j: Index);
}

/// This is a workaround of current Rust limitations.
///
/// Traits of objects which can be iterated through like a vector.
pub trait Iterable<N> {
    /// Gets a vector-like read-only iterator.
    fn iter<'l>(&'l self) -> VecIterator<'l, N>;
}

/// This is a workaround of current Rust limitations.
///
/// Traits of mutable objects which can be iterated through like a vector.
pub trait IterableMut<N> {
    /// Gets a vector-like read-write iterator.
    fn mut_iter<'l>(&'l mut self) -> VecMutIterator<'l, N>;
}
<commit_msg>Move the Basis trait inheritance from VecExt to AlgebraicVecExt.<commit_after>//! Traits giving structural informations on linear algebra objects or the space they live in.

use std::num::Zero;
use std::vec::{VecIterator, VecMutIterator};
use traits::operations::{RMul, LMul, ScalarAdd, ScalarSub};
use traits::geometry::{Dot, Norm, UniformSphereSample};

/// Trait of matrices.
///
/// A matrix has rows and columns and are able to multiply them.
pub trait Mat<R, C> : Row<R> + Col<C> + RMul<R> + LMul<C> { }

impl<M: Row<R> + Col<C> + RMul<R> + LMul<C>, R, C> Mat<R, C> for M {
}

/// Trait of matrices which can be converted to another matrix.
///
/// Use this to change easily the type of a matrix components.
pub trait MatCast<M> {
    /// Converts `m` to have the type `M`.
    fn from(m: Self) -> M;
}

// XXX: we keep ScalarAdd and ScalarSub here to avoid trait impl conflict (overriding) between the
// different Add/Sub traits. This is _so_ unfortunate…

// NOTE: cant call that `Vector` because it conflicts with std::Vector
/// Trait grouping most common operations on vectors.
pub trait Vec<N>: Dim + Sub<Self, Self> + Add<Self, Self> + Neg<Self> + Zero + Eq + Mul<N, Self>
                  + Div<N, Self> + Dot<N> {
}

/// Trait of vector with components implementing the `Algebraic` trait.
pub trait AlgebraicVec<N: Algebraic>: Vec<N> + Norm<N> {
}

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait VecExt<N>: Vec<N> + Indexable<uint, N> + Iterable<N> + Round +
                     UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable
{ }

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait AlgebraicVecExt<N: Algebraic>: AlgebraicVec<N> + VecExt<N> + Basis { }

impl<N, V: Dim + Sub<V, V> + Add<V, V> + Neg<V> + Zero + Eq + Mul<N, V> + Div<N, V> + Dot<N>>
Vec<N> for V { }

impl<N: Algebraic, V: Vec<N> + Norm<N>> AlgebraicVec<N> for V { }

impl<N,
     V: Vec<N> + Indexable<uint, N> + Iterable<N> + Round +
        UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable>
VecExt<N> for V { }

impl<N: Algebraic, V: AlgebraicVec<N> + VecExt<N> + Basis> AlgebraicVecExt<N> for V { }

/// Trait of vectors which can be converted to another vector. Used to change the type of a vector
/// components.
pub trait VecCast<V> {
    /// Converts `v` to have the type `V`.
    fn from(v: Self) -> V;
}

// FIXME: return an iterator instead
/// Traits of objects which can form a basis (typically vectors).
pub trait Basis {
    /// Iterates through the canonical basis of the space in which this object lives.
    fn canonical_basis(&fn(Self) -> bool);

    /// Iterates through a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis(&self, &fn(Self) -> bool);

    /// Creates the canonical basis of the space in which this object lives.
    fn canonical_basis_list() -> ~[Self] {
        let mut res = ~[];

        do Basis::canonical_basis |elem| {
            res.push(elem);

            true
        }

        res
    }

    /// Creates a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis_list(&self) -> ~[Self] {
        let mut res = ~[];

        do self.orthonormal_subspace_basis |elem| {
            res.push(elem);

            true
        }

        res
    }
}

/// Trait to access rows of a matrix or a vector.
pub trait Row<R> {
    /// The number of column of `self`.
    fn num_rows(&self) -> uint;
    /// Reads the `i`-th row of `self`.
    fn row(&self, i: uint) -> R;
    /// Writes the `i`-th row of `self`.
    fn set_row(&mut self, i: uint, R);

    // FIXME: add iterators on rows: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait to access columns of a matrix or vector.
pub trait Col<C> {
    /// The number of column of this matrix or vector.
    fn num_cols(&self) -> uint;

    /// Reads the `i`-th column of `self`.
    fn col(&self, i: uint) -> C;

    /// Writes the `i`-th column of `self`.
    fn set_col(&mut self, i: uint, C);

    // FIXME: add iterators on columns: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait of objects having a spacial dimension known at compile time.
pub trait Dim {
    /// The dimension of the object.
    fn dim(unused_self: Option<Self>) -> uint;
}

// FIXME: this trait should not be on nalgebra.
// however, it is needed because std::ops::Index is (strangely) to poor: it
// does not have a function to set values.
// Also, using Index with tuples crashes.
/// This is a workaround of current Rust limitations.
///
/// It exists because the `Index` trait cannot be used to express write access.
/// Thus, this is the same as the `Index` trait but without the syntactic sugar and with a method
/// to write to a specific index.
pub trait Indexable<Index, Res> {
    /// Reads the `i`-th element of `self`.
    fn at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    fn set(&mut self, i: Index, Res);
    /// Swaps the `i`-th element of `self` with its `j`-th element.
    fn swap(&mut self, i: Index, j: Index);
}

/// This is a workaround of current Rust limitations.
///
/// Traits of objects which can be iterated through like a vector.
pub trait Iterable<N> {
    /// Gets a vector-like read-only iterator.
    fn iter<'l>(&'l self) -> VecIterator<'l, N>;
}

/// This is a workaround of current Rust limitations.
///
/// Traits of mutable objects which can be iterated through like a vector.
pub trait IterableMut<N> {
    /// Gets a vector-like read-write iterator.
    fn mut_iter<'l>(&'l mut self) -> VecMutIterator<'l, N>;
}
<|endoftext|>