<commit_before>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//! Tool that converts a jed to a json file

use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

extern crate xc2bit;
use xc2bit::*;

extern crate yosys_netlist_json;
use yosys_netlist_json::*;

fn main() {
    let args = ::std::env::args().collect::<Vec<_>>();

    if args.len() != 2 {
        println!("Usage: {} file.jed", args[0]);
        ::std::process::exit(1);
    }

    // Load the jed
    let mut f = File::open(&args[1]).expect("failed to open file");
    let mut data = Vec::new();
    f.read_to_end(&mut data).expect("failed to read data");

    let bits_result = read_jed(&data);
    let (bits, device_name_option) = bits_result.expect("failed to read jed");
    let device_name = device_name_option.expect("missing device name in jed");

    let bitstream_result = XC2Bitstream::from_jed(&bits, &device_name);
    let bitstream = bitstream_result.expect("failed to process jed");

    // Because of how the closures work, we unfortunately need to use a RefCell here
    let output_netlist = RefCell::new(Netlist::default());
    let node_vec = RefCell::new(Vec::new());
    // Skip 0 and 1 just like yosys does
    let wire_idx = RefCell::new(2);

    // Create common/global stuff ahead of time
    {
        let mut output_netlist_mut = output_netlist.borrow_mut();
        // TOOD: Proper version number
        output_netlist_mut.creator = String::from("xc2bit xc2jed2json utility - DEVELOPMENT VERSION");

        let mut module = Module::default();
        module.attributes.insert(String::from("PART_NAME"),
            AttributeVal::S(format!("{}", bitstream.bits.device_type())));
        module.attributes.insert(String::from("PART_SPEED"),
            AttributeVal::S(format!("{}", bitstream.speed_grade)));
        module.attributes.insert(String::from("PART_PKG"),
            AttributeVal::S(format!("{}", bitstream.package)));

        output_netlist_mut.modules.insert(String::from("top"), module);
    }

    // Call the giant structure callback
    get_device_structure(bitstream.bits.device_type(),
        |node_name: &str, node_type: &str, fb: u32, idx: u32| {
            // Start constructing the cell object
            let cell;

            match node_type {
                "BUFG" | "BUFGSR" | "BUFGTS" => {
                    let mut connections = HashMap::new();
                    connections.insert(String::from("I"), Vec::new());
                    connections.insert(String::from("O"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes: HashMap::new(),
                        port_directions: HashMap::new(),
                        connections: connections,
                    }
                },
                "IOBUFE" => {
                    let (fb, mc) = iob_num_to_fb_mc_num(bitstream.bits.device_type(), idx).unwrap();

                    // Construct the wire for the toplevel port
                    let toplevel_wire = {
                        let orig_wire_idx = {*wire_idx.borrow()};
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
                        netnames.insert(format!("PAD_FB{}_{}", fb + 1, mc + 1), Netname {
                            hide_name: 0,
                            bits: vec![BitVal::N(orig_wire_idx)],
                            attributes: HashMap::new(),
                        });

                        // Return the current wire index
                        *wire_idx.borrow_mut() += 1;
                        orig_wire_idx
                    };

                    // Construct the toplevel port
                    {
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut ports = &mut output_netlist_mut.modules.get_mut("top").unwrap().ports;
                        ports.insert(format!("PAD_FB{}_{}", fb + 1, mc + 1), Port {
                            direction: PortDirection::InOut,
                            bits: vec![BitVal::N(toplevel_wire)],
                        });
                    }

                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, mc + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("I"), Vec::new());
                    connections.insert(String::from("E"), Vec::new());
                    connections.insert(String::from("O"), Vec::new());
                    connections.insert(String::from("IO"), vec![BitVal::N(toplevel_wire)]);
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                // FIXME: Boilerplate, copy-pasta
                "IBUF" => {
                    // Construct the wire for the toplevel port
                    let toplevel_wire = {
                        let orig_wire_idx = {*wire_idx.borrow()};
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
                        netnames.insert(String::from("INPAD"), Netname {
                            hide_name: 0,
                            bits: vec![BitVal::N(orig_wire_idx)],
                            attributes: HashMap::new(),
                        });

                        // Return the current wire index
                        *wire_idx.borrow_mut() += 1;
                        orig_wire_idx
                    };

                    // Construct the toplevel port
                    {
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut ports = &mut output_netlist_mut.modules.get_mut("top").unwrap().ports;
                        ports.insert(String::from("INPAD"), Port {
                            direction: PortDirection::Input,
                            bits: vec![BitVal::N(toplevel_wire)],
                        });
                    }

                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(String::from("INPAD")));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("O"), Vec::new());
                    connections.insert(String::from("I"), vec![BitVal::N(toplevel_wire)]);
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "ANDTERM" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_P{}", fb + 1, idx)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN"), Vec::new());
                    connections.insert(String::from("IN_B"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "ORTERM" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "MACROCELL_XOR" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN_PTC"), Vec::new());
                    connections.insert(String::from("IN_ORTERM"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "REG" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("Q"), Vec::new());
                    connections.insert(String::from("PRE"), Vec::new());
                    connections.insert(String::from("CLR"), Vec::new());

                    let cell_type;
                    let mc = &bitstream.bits.get_fb()[fb as usize].mcs[idx as usize];
                    match mc.reg_mode {
                        XC2MCRegMode::DFF => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FDDCP");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FDCP");
                                } else {
                                    cell_type = String::from("FDCP_N");
                                }
                            }
                        },
                        XC2MCRegMode::LATCH => {
                            connections.insert(String::from("G"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());

                            if !mc.clk_invert_pol {
                                cell_type = String::from("LDCP");
                            } else {
                                cell_type = String::from("LDCP_N");
                            }
                        },
                        XC2MCRegMode::TFF => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("T"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FTDCP");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FTCP");
                                } else {
                                    cell_type = String::from("FTCP_N");
                                }
                            }
                        },
                        XC2MCRegMode::DFFCE => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());
                            connections.insert(String::from("CE"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FDDCPE");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FDCPE");
                                } else {
                                    cell_type = String::from("FDCPE_N");
                                }
                            }
                        }
                    }

                    cell = Cell {
                        hide_name: 0,
                        cell_type,
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                _ => unreachable!()
            }

            // Create the cell in the output module
            let mut output_netlist_mut = output_netlist.borrow_mut();
            let mut cells = &mut output_netlist_mut.modules.get_mut("top").unwrap().cells;
            cells.insert(node_name.to_owned(), cell);

            // Memoization needed for the callback interface
            let mut node_vec = node_vec.borrow_mut();
            node_vec.push((node_name.to_owned(), node_type.to_owned(), fb, idx));
            node_vec.len() - 1
        },
        |wire_name: &str| {
            // Create the net in the output module
            let orig_wire_idx = {*wire_idx.borrow()};
            let mut output_netlist_mut = output_netlist.borrow_mut();
            let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
            netnames.insert(wire_name.to_owned(), Netname {
                hide_name: 0,
                bits: vec![BitVal::N(orig_wire_idx)],
                attributes: HashMap::new(),
            });

            // Return the current wire index
            *wire_idx.borrow_mut() += 1;
            orig_wire_idx
        },
        |node_ref: usize, wire_ref: usize, port_name: &str, port_idx: u32| {
        }
    );

    // Write the final output
    output_netlist.borrow().to_writer(&mut ::std::io::stdout()).expect("failed to write json");
}
<commit_msg>xc2bit-jed2json: Write attributes of IOBs to json<commit_after>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//! Tool that converts a jed to a json file

use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

extern crate xc2bit;
use xc2bit::*;

extern crate yosys_netlist_json;
use yosys_netlist_json::*;

fn main() {
    let args = ::std::env::args().collect::<Vec<_>>();

    if args.len() != 2 {
        println!("Usage: {} file.jed", args[0]);
        ::std::process::exit(1);
    }

    // Load the jed
    let mut f = File::open(&args[1]).expect("failed to open file");
    let mut data = Vec::new();
    f.read_to_end(&mut data).expect("failed to read data");

    let bits_result = read_jed(&data);
    let (bits, device_name_option) = bits_result.expect("failed to read jed");
    let device_name = device_name_option.expect("missing device name in jed");

    let bitstream_result = XC2Bitstream::from_jed(&bits, &device_name);
    let bitstream = bitstream_result.expect("failed to process jed");

    // Because of how the closures work, we unfortunately need to use a RefCell here
    let output_netlist = RefCell::new(Netlist::default());
    let node_vec = RefCell::new(Vec::new());
    // Skip 0 and 1 just like yosys does
    let wire_idx = RefCell::new(2);

    // Create common/global stuff ahead of time
    {
        let mut output_netlist_mut = output_netlist.borrow_mut();
        // TOOD: Proper version number
        output_netlist_mut.creator = String::from("xc2bit xc2jed2json utility - DEVELOPMENT VERSION");

        let mut module = Module::default();
        module.attributes.insert(String::from("PART_NAME"),
            AttributeVal::S(format!("{}", bitstream.bits.device_type())));
        module.attributes.insert(String::from("PART_SPEED"),
            AttributeVal::S(format!("{}", bitstream.speed_grade)));
        module.attributes.insert(String::from("PART_PKG"),
            AttributeVal::S(format!("{}", bitstream.package)));

        output_netlist_mut.modules.insert(String::from("top"), module);
    }

    // Call the giant structure callback
    get_device_structure(bitstream.bits.device_type(),
        |node_name: &str, node_type: &str, fb: u32, idx: u32| {
            // Start constructing the cell object
            let cell;

            match node_type {
                "BUFG" | "BUFGSR" | "BUFGTS" => {
                    let mut connections = HashMap::new();
                    connections.insert(String::from("I"), Vec::new());
                    connections.insert(String::from("O"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes: HashMap::new(),
                        port_directions: HashMap::new(),
                        connections: connections,
                    }
                },
                "IOBUFE" => {
                    let (fb, mc) = iob_num_to_fb_mc_num(bitstream.bits.device_type(), idx).unwrap();

                    // Construct the wire for the toplevel port
                    let toplevel_wire = {
                        let orig_wire_idx = {*wire_idx.borrow()};
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
                        netnames.insert(format!("PAD_FB{}_{}", fb + 1, mc + 1), Netname {
                            hide_name: 0,
                            bits: vec![BitVal::N(orig_wire_idx)],
                            attributes: HashMap::new(),
                        });

                        // Return the current wire index
                        *wire_idx.borrow_mut() += 1;
                        orig_wire_idx
                    };

                    // Construct the toplevel port
                    {
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut ports = &mut output_netlist_mut.modules.get_mut("top").unwrap().ports;
                        ports.insert(format!("PAD_FB{}_{}", fb + 1, mc + 1), Port {
                            direction: PortDirection::InOut,
                            bits: vec![BitVal::N(toplevel_wire)],
                        });
                    }

                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, mc + 1)));
                    // Grab attributes
                    if let Some(iobs) = bitstream.bits.get_small_iobs() {
                        if iobs[idx as usize].slew_is_fast {
                            attributes.insert(String::from("SLEW"), AttributeVal::S(String::from("FAST")));
                        } else {
                            attributes.insert(String::from("SLEW"), AttributeVal::S(String::from("SLOW")));
                        }

                        if iobs[idx as usize].slew_is_fast {
                            attributes.insert(String::from("TERM"), AttributeVal::S(String::from("TRUE")));
                        } else {
                            attributes.insert(String::from("TERM"), AttributeVal::S(String::from("FALSE")));
                        }

                        if iobs[idx as usize].obuf_mode == XC2IOBOBufMode::CGND {
                            attributes.insert(String::from("CGND"), AttributeVal::N(1));
                        }

                        if iobs[idx as usize].schmitt_trigger {
                            attributes.insert(String::from("SCHMITT_TRIGGER"), AttributeVal::S(String::from("TRUE")));
                        } else {
                            attributes.insert(String::from("SCHMITT_TRIGGER"), AttributeVal::S(String::from("FALSE")));
                        }
                    }
                    if let Some(iobs) = bitstream.bits.get_large_iobs() {
                        if iobs[idx as usize].slew_is_fast {
                            attributes.insert(String::from("SLEW"), AttributeVal::S(String::from("FAST")));
                        } else {
                            attributes.insert(String::from("SLEW"), AttributeVal::S(String::from("SLOW")));
                        }

                        if iobs[idx as usize].slew_is_fast {
                            attributes.insert(String::from("TERM"), AttributeVal::S(String::from("TRUE")));
                        } else {
                            attributes.insert(String::from("TERM"), AttributeVal::S(String::from("FALSE")));
                        }

                        if iobs[idx as usize].obuf_mode == XC2IOBOBufMode::CGND {
                            attributes.insert(String::from("CGND"), AttributeVal::N(1));
                        }

                        match iobs[idx as usize].ibuf_mode {
                            XC2IOBIbufMode::NoVrefNoSt => {
                                attributes.insert(String::from("SCHMITT_TRIGGER"), AttributeVal::S(String::from("FALSE")));
                            },
                            XC2IOBIbufMode::NoVrefSt => {
                                attributes.insert(String::from("SCHMITT_TRIGGER"), AttributeVal::S(String::from("TRUE")));
                            },
                            XC2IOBIbufMode::UsesVref => {
                                // FIXME
                                unimplemented!();
                            },
                            XC2IOBIbufMode::IsVref => {
                                // FIXME
                                unimplemented!();
                            }
                        }

                        if iobs[idx as usize].uses_data_gate {
                            // FIXME
                            unimplemented!();
                        }
                    }

                    let mut connections = HashMap::new();
                    connections.insert(String::from("I"), Vec::new());
                    connections.insert(String::from("E"), Vec::new());
                    connections.insert(String::from("O"), Vec::new());
                    connections.insert(String::from("IO"), vec![BitVal::N(toplevel_wire)]);
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                // FIXME: Boilerplate, copy-pasta
                "IBUF" => {
                    // Construct the wire for the toplevel port
                    let toplevel_wire = {
                        let orig_wire_idx = {*wire_idx.borrow()};
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
                        netnames.insert(String::from("INPAD"), Netname {
                            hide_name: 0,
                            bits: vec![BitVal::N(orig_wire_idx)],
                            attributes: HashMap::new(),
                        });

                        // Return the current wire index
                        *wire_idx.borrow_mut() += 1;
                        orig_wire_idx
                    };

                    // Construct the toplevel port
                    {
                        let mut output_netlist_mut = output_netlist.borrow_mut();
                        let mut ports = &mut output_netlist_mut.modules.get_mut("top").unwrap().ports;
                        ports.insert(String::from("INPAD"), Port {
                            direction: PortDirection::Input,
                            bits: vec![BitVal::N(toplevel_wire)],
                        });
                    }

                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(String::from("INPAD")));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("O"), Vec::new());
                    connections.insert(String::from("I"), vec![BitVal::N(toplevel_wire)]);
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "ANDTERM" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_P{}", fb + 1, idx)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN"), Vec::new());
                    connections.insert(String::from("IN_B"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "ORTERM" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "MACROCELL_XOR" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("IN_PTC"), Vec::new());
                    connections.insert(String::from("IN_ORTERM"), Vec::new());
                    connections.insert(String::from("OUT"), Vec::new());
                    cell = Cell {
                        hide_name: 0,
                        cell_type: node_type.to_owned(),
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                "REG" => {
                    let mut attributes = HashMap::new();
                    attributes.insert(String::from("LOC"), AttributeVal::S(format!("FB{}_{}", fb + 1, idx + 1)));
                    let mut connections = HashMap::new();
                    connections.insert(String::from("Q"), Vec::new());
                    connections.insert(String::from("PRE"), Vec::new());
                    connections.insert(String::from("CLR"), Vec::new());

                    let cell_type;
                    let mc = &bitstream.bits.get_fb()[fb as usize].mcs[idx as usize];
                    match mc.reg_mode {
                        XC2MCRegMode::DFF => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FDDCP");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FDCP");
                                } else {
                                    cell_type = String::from("FDCP_N");
                                }
                            }
                        },
                        XC2MCRegMode::LATCH => {
                            connections.insert(String::from("G"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());

                            if !mc.clk_invert_pol {
                                cell_type = String::from("LDCP");
                            } else {
                                cell_type = String::from("LDCP_N");
                            }
                        },
                        XC2MCRegMode::TFF => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("T"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FTDCP");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FTCP");
                                } else {
                                    cell_type = String::from("FTCP_N");
                                }
                            }
                        },
                        XC2MCRegMode::DFFCE => {
                            connections.insert(String::from("C"), Vec::new());
                            connections.insert(String::from("D"), Vec::new());
                            connections.insert(String::from("CE"), Vec::new());

                            if mc.is_ddr {
                                cell_type = String::from("FDDCPE");
                            } else {
                                if !mc.clk_invert_pol {
                                    cell_type = String::from("FDCPE");
                                } else {
                                    cell_type = String::from("FDCPE_N");
                                }
                            }
                        }
                    }

                    cell = Cell {
                        hide_name: 0,
                        cell_type,
                        parameters: HashMap::new(),
                        attributes,
                        port_directions: HashMap::new(),
                        connections,
                    }
                },
                _ => unreachable!()
            }

            // Create the cell in the output module
            let mut output_netlist_mut = output_netlist.borrow_mut();
            let mut cells = &mut output_netlist_mut.modules.get_mut("top").unwrap().cells;
            cells.insert(node_name.to_owned(), cell);

            // Memoization needed for the callback interface
            let mut node_vec = node_vec.borrow_mut();
            node_vec.push((node_name.to_owned(), node_type.to_owned(), fb, idx));
            node_vec.len() - 1
        },
        |wire_name: &str| {
            // Create the net in the output module
            let orig_wire_idx = {*wire_idx.borrow()};
            let mut output_netlist_mut = output_netlist.borrow_mut();
            let mut netnames = &mut output_netlist_mut.modules.get_mut("top").unwrap().netnames;
            netnames.insert(wire_name.to_owned(), Netname {
                hide_name: 0,
                bits: vec![BitVal::N(orig_wire_idx)],
                attributes: HashMap::new(),
            });

            // Return the current wire index
            *wire_idx.borrow_mut() += 1;
            orig_wire_idx
        },
        |node_ref: usize, wire_ref: usize, port_name: &str, port_idx: u32| {
        }
    );

    // Write the final output
    output_netlist.borrow().to_writer(&mut ::std::io::stdout()).expect("failed to write json");
}
<|endoftext|>


<commit_before>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {
        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    use tdvalid::*;
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"
                                    if VkBlock::Types == cur_block =>
                                    if let Some(category) = find_attribute(tag_attrs, "category") {
                                        registry.push_type(type_buffer).ok();
                                        match category {
                                            "basetype"       |
                                            "bitmask"       => type_buffer = VkType::empty_typedef(),
                                            "define"        => type_buffer = VkType::Unhandled,
                                            "enum"          => type_buffer = VkType::Unhandled,
                                            "funcpointer"   => type_buffer = VkType::Unhandled,
                                            "group"         => type_buffer = VkType::Unhandled,
                                            "handle"        => type_buffer = VkType::empty_handle(),
                                            "include"       => type_buffer = VkType::Unhandled,
                                            "struct"        =>
                                                type_buffer = VkType::new_struct(registry.append_str(find_attribute(tag_attrs, "name").unwrap()), Vec::with_capacity(8)),
                                            "union"         =>
                                                type_buffer = VkType::new_union(registry.append_str(find_attribute(tag_attrs, "name").unwrap()), Vec::with_capacity(8)),
                                            _               => panic!("Unexpected category")
                                        }
                                    },
                                "type"       => (),

                                "member"
                                    if VkBlock::Types == cur_block =>
                                    match type_buffer {
                                        VkType::Struct{fields: ref mut members, ..}   |
                                        VkType::Union{variants: ref mut members, ..} => members.push(VkMember::empty()),
                                        _                                            => panic!("Unexpected \"member\" tag found")
                                    },
                                "member"     => panic!("\"member\" tag found outside of \"types\" block"),

                                _ => ()
                            },

                        Characters{ref chars, tag}
                            if VkBlock::Types == cur_block => {
                            let chars = &chars[..];
                            match type_buffer {
                                VkType::Struct{fields: ref mut members, ..} |
                                VkType::Union{variants: ref mut members, ..} =>
                                    match tag {
                                        "member" =>
                                            match chars {
                                                "const" => members.last_mut().unwrap().change_type_const(),
                                                "*"     => members.last_mut().unwrap().change_type_ptr(),
                                                _
                                                    if &chars[0..1] == "[" =>
                                                    match parse_array_index(chars) {
                                                        Some((size, _)) => {members.last_mut().unwrap().change_type_array(size);}
                                                        None => panic!(format!("Unexpected characters after name: {}", chars))
                                                    },
                                                _       => ()
                                            },
                                        "type"   => members.last_mut().unwrap().set_type(registry.append_str(chars)),
                                        "name"   =>
                                            if let Some((size, name_len)) = parse_array_index(chars) {
                                                members.last_mut().unwrap().change_type_array(size)
                                                                           .set_name(registry.append_str(&chars[..name_len]))
                                            } else {members.last_mut().unwrap().set_name(registry.append_str(chars))},
                                        "enum"   => members.last_mut().unwrap().change_type_array_enum(registry.append_str(chars)),
                                        _        => ()
                                    },
                                VkType::TypeDef{ref mut ty,
                                                ref mut name,
                                                ref mut validity} =>
                                    match tag {
                                        "type" =>
                                            match chars {
                                                "typedef" => *validity ^= NOTYPEDEF,
                                                ";"       => *validity ^= NOSEMICOLON,
                                                _         => *ty = registry.append_str(chars)
                                            },
                                        "name" => *name = registry.append_str(chars),
                                        _ => panic!("Unexpected tag")
                                    },
                                VkType::Handle{ref mut name,
                                               ref mut validity,
                                               ref mut dispatchable} =>
                                    match tag {
                                        "type" =>
                                            match chars {
                                                "VK_DEFINE_HANDLE"                  =>  *validity = true,
                                                "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => {*validity = true; *dispatchable = false}
                                                "(" |
                                                ")" => (),
                                                _                                   => panic!("Unexpected handle")
                                            },
                                        "name" => *name = registry.append_str(chars),
                                        _ => ()
                                    },
                                _ => ()
                            }
                        }
                        Characters{..} => ()
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..}    => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, ref fields}  => {
                    println!("Struct {:?}", CStr::from_ptr(name));
                    for f in fields {
                        println!("\t{:?}", f);
                    }
                }
                VkType::Union{name, ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));
                    for v in variants {
                        println!("\t{:?}", v);
                    }
                }
                VkType::TypeDef{ty, name, validity} =>
                    if validity != 0 {
                        panic!("Invalid typedef")
                    } else {
                        println!("TypeDef {:?} {:?}", CStr::from_ptr(ty), CStr::from_ptr(name))
                    },
                VkType::Handle{name, validity, dispatchable} =>
                    if !validity {
                        panic!("Invalid handle")
                    } else if dispatchable {
                        println!("Handle {:?}", CStr::from_ptr(name))
                    } else {
                        println!("Non-Dispatchable Handle {:?}", CStr::from_ptr(name))
                    },
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    if let Some(el) = vk_elements.pop() {
        if let XmlElement::Characters{..} = el {
            pop_element_stack(vk_elements)
        }
    } else {panic!("Invalid xml; Unexpected closing tag")}
}

/// Takes a string slice and extracts x from [x], as long as [x] is at the end.
///
///
/// Returns (x, length of `chars` without [x]) if "[x]" is detected
///
/// Returns (0, 0) if "[" is detected
fn parse_array_index(chars: &str) -> Option<(usize, usize)> {
    let mut chariter = chars.chars().rev();
    match chariter.next().unwrap() {
        // This only occurs when the variable is going to be an array. Now, in most cases it appears
        // *outside* of <name>, but some dumbass decided to have two fields in the entire xml contain it
        // inside. To the person who did that (you know who you are): if you're writing a document designed
        // to be easy to parse, PLEASE keep it consistent. This goes for everyone else as well - think of
        // the person that has to write an interpreter for your goddamn file.
        ']' => {
            // The size of the array
            let mut size = 0;
            for (i, digit) in chariter.clone().take_while(|c| c.is_digit(10)).enumerate() {
                size += digit.to_digit(10).unwrap() as usize * (10usize.pow(i as u32));
            }

            // The length of the actual name, dropping the array length. Is decremented in the iterator below.
            let mut name_len = chars.len()-1;
            match chariter.skip_while(|c| {name_len -= 1;
                                           c.is_digit(10) | c.is_whitespace()}
                                           ).next() {
                Some('[') => (),
                Some(c)   => panic!(format!("Expected '['; found '{}'", c)),
                None      => panic!("Expected '['; found nothing")
            }
            Some((size, name_len))
        }
        '[' => Some((0, 0)),
        _   => None
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<commit_msg>Fixed bug where registry would not pick up last crawled type<commit_after>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {
        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    use tdvalid::*;
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"
                                    if VkBlock::Types == cur_block =>
                                    if let Some(category) = find_attribute(tag_attrs, "category") {
                                        registry.push_type(type_buffer).ok();
                                        match category {
                                            "basetype"       |
                                            "bitmask"       => type_buffer = VkType::empty_typedef(),
                                            "define"        => type_buffer = VkType::Unhandled,
                                            "enum"          => type_buffer = VkType::Unhandled,
                                            "funcpointer"   => type_buffer = VkType::Unhandled,
                                            "group"         => type_buffer = VkType::Unhandled,
                                            "handle"        => type_buffer = VkType::empty_handle(),
                                            "include"       => type_buffer = VkType::Unhandled,
                                            "struct"        =>
                                                type_buffer = VkType::new_struct(registry.append_str(find_attribute(tag_attrs, "name").unwrap()), Vec::with_capacity(8)),
                                            "union"         =>
                                                type_buffer = VkType::new_union(registry.append_str(find_attribute(tag_attrs, "name").unwrap()), Vec::with_capacity(8)),
                                            _               => panic!("Unexpected category")
                                        }
                                    },
                                "type"       => (),

                                "member"
                                    if VkBlock::Types == cur_block =>
                                    match type_buffer {
                                        VkType::Struct{fields: ref mut members, ..}   |
                                        VkType::Union{variants: ref mut members, ..} => members.push(VkMember::empty()),
                                        _                                            => panic!("Unexpected \"member\" tag found")
                                    },
                                "member"     => panic!("\"member\" tag found outside of \"types\" block"),

                                _ => ()
                            },

                        Characters{ref chars, tag}
                            if VkBlock::Types == cur_block => {
                            let chars = &chars[..];
                            match type_buffer {
                                VkType::Struct{fields: ref mut members, ..} |
                                VkType::Union{variants: ref mut members, ..} =>
                                    match tag {
                                        "member" =>
                                            match chars {
                                                "const" => members.last_mut().unwrap().change_type_const(),
                                                "*"     => members.last_mut().unwrap().change_type_ptr(),
                                                _
                                                    if &chars[0..1] == "[" =>
                                                    match parse_array_index(chars) {
                                                        Some((size, _)) => {members.last_mut().unwrap().change_type_array(size);}
                                                        None => panic!(format!("Unexpected characters after name: {}", chars))
                                                    },
                                                _       => ()
                                            },
                                        "type"   => members.last_mut().unwrap().set_type(registry.append_str(chars)),
                                        "name"   =>
                                            if let Some((size, name_len)) = parse_array_index(chars) {
                                                members.last_mut().unwrap().change_type_array(size)
                                                                           .set_name(registry.append_str(&chars[..name_len]))
                                            } else {members.last_mut().unwrap().set_name(registry.append_str(chars))},
                                        "enum"   => members.last_mut().unwrap().change_type_array_enum(registry.append_str(chars)),
                                        _        => ()
                                    },
                                VkType::TypeDef{ref mut ty,
                                                ref mut name,
                                                ref mut validity} =>
                                    match tag {
                                        "type" =>
                                            match chars {
                                                "typedef" => *validity ^= NOTYPEDEF,
                                                ";"       => *validity ^= NOSEMICOLON,
                                                _         => *ty = registry.append_str(chars)
                                            },
                                        "name" => *name = registry.append_str(chars),
                                        _ => panic!("Unexpected tag")
                                    },
                                VkType::Handle{ref mut name,
                                               ref mut validity,
                                               ref mut dispatchable} =>
                                    match tag {
                                        "type" =>
                                            match chars {
                                                "VK_DEFINE_HANDLE"                  =>  *validity = true,
                                                "VK_DEFINE_NON_DISPATCHABLE_HANDLE" => {*validity = true; *dispatchable = false}
                                                "(" |
                                                ")" => (),
                                                _                                   => panic!("Unexpected handle")
                                            },
                                        "name" => *name = registry.append_str(chars),
                                        _ => ()
                                    },
                                _ => ()
                            }
                        }
                        Characters{..} => ()
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..}    => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    // The loop might not push the last type, so it's handled here.
    registry.push_type(type_buffer).ok();

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, ref fields}  => {
                    println!("Struct {:?}", CStr::from_ptr(name));
                    for f in fields {
                        println!("\t{:?}", f);
                    }
                }
                VkType::Union{name, ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));
                    for v in variants {
                        println!("\t{:?}", v);
                    }
                }
                VkType::TypeDef{ty, name, validity} =>
                    if validity != 0 {
                        panic!("Invalid typedef")
                    } else {
                        println!("TypeDef {:?} {:?}", CStr::from_ptr(ty), CStr::from_ptr(name))
                    },
                VkType::Handle{name, validity, dispatchable} =>
                    if !validity {
                        panic!("Invalid handle")
                    } else if dispatchable {
                        println!("Handle {:?}", CStr::from_ptr(name))
                    } else {
                        println!("Non-Dispatchable Handle {:?}", CStr::from_ptr(name))
                    },
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    if let Some(el) = vk_elements.pop() {
        if let XmlElement::Characters{..} = el {
            pop_element_stack(vk_elements)
        }
    } else {panic!("Invalid xml; Unexpected closing tag")}
}

/// Takes a string slice and extracts x from [x], as long as [x] is at the end.
///
///
/// Returns (x, length of `chars` without [x]) if "[x]" is detected
///
/// Returns (0, 0) if "[" is detected
fn parse_array_index(chars: &str) -> Option<(usize, usize)> {
    let mut chariter = chars.chars().rev();
    match chariter.next().unwrap() {
        // This only occurs when the variable is going to be an array. Now, in most cases it appears
        // *outside* of <name>, but some dumbass decided to have two fields in the entire xml contain it
        // inside. To the person who did that (you know who you are): if you're writing a document designed
        // to be easy to parse, PLEASE keep it consistent. This goes for everyone else as well - think of
        // the person that has to write an interpreter for your goddamn file.
        ']' => {
            // The size of the array
            let mut size = 0;
            for (i, digit) in chariter.clone().take_while(|c| c.is_digit(10)).enumerate() {
                size += digit.to_digit(10).unwrap() as usize * (10usize.pow(i as u32));
            }

            // The length of the actual name, dropping the array length. Is decremented in the iterator below.
            let mut name_len = chars.len()-1;
            match chariter.skip_while(|c| {name_len -= 1;
                                           c.is_digit(10) | c.is_whitespace()}
                                           ).next() {
                Some('[') => (),
                Some(c)   => panic!(format!("Expected '['; found '{}'", c)),
                None      => panic!("Expected '['; found nothing")
            }
            Some((size, name_len))
        }
        '[' => Some((0, 0)),
        _   => None
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<|endoftext|>


<commit_before>extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate toml;
extern crate tokio;
extern crate tokio_process;
#[macro_use] extern crate futures;
#[macro_use] extern crate log;
extern crate bytes;

use std::collections::HashMap;
use futures::Future;
use futures::future::Either;
use std::path::Path;
use std::fmt::Display;
use std::sync::Arc;

pub mod util;
mod eval;

#[derive(Clone, Serialize, Deserialize, Default, PartialEq, Debug)]
struct EvalServiceCfg {
    timeout: usize,
    languages: HashMap<String, LanguageCfg>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct LanguageCfg {
    code_before: Option<String>,
    code_after: Option<String>,
    timeout: Option<usize>,
    #[serde(flatten)]
    backend: BackendCfg
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum BackendCfg {
    Exec(ExecBackend),
    Network(NetworkBackend),
    UnixSocket(UnixSocketBackend)
}

#[derive(Clone, Debug)]
pub struct EvalService {
    timeout: usize,
    languages: HashMap<String, Arc<Language>>
}

#[derive(Clone, PartialEq, Debug)]
pub struct Language {
    name: String,
    code_before: Option<String>,
    code_after: Option<String>,
    timeout: Option<usize>,
    backend: Backend
}

#[derive(Clone, PartialEq, Debug)]
enum Backend {
    Exec(Arc<ExecBackend>),
    Network(Arc<NetworkBackend>),
    UnixSocket(Arc<UnixSocketBackend>)
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct ExecBackend {
    cmdline: Vec<String>,
    timeout_prefix: Option<String>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct NetworkBackend {
    network_addr: String,
    timeout_cmdline: Option<Vec<String>>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct UnixSocketBackend {
    socket_addr: String,
    timeout_cmdline: Option<Vec<String>>
}

impl Language {
    fn from(name: String, default_timeout: usize, cfg: LanguageCfg) -> Self {
        Language {
            name,
            code_before: cfg.code_before,
            code_after: cfg.code_after,
            timeout: cfg.timeout.or_else(|| Some(default_timeout)),
            backend: match cfg.backend {
                BackendCfg::Exec(x) => Backend::Exec(Arc::new(x)),
                BackendCfg::Network(x) => Backend::Network(Arc::new(x)),
                BackendCfg::UnixSocket(x) => Backend::UnixSocket(Arc::new(x)),
            }
        }
    }
}

impl EvalService {
    fn fixup(cfg: EvalServiceCfg) -> Self {
        debug!("Loaded config: {:#?}", cfg);
        let mut new = EvalService {
            timeout: cfg.timeout,
            languages: HashMap::new()
        };
        let timeout = cfg.timeout;
        for (name, lang) in cfg.languages.into_iter() {
            new.languages.insert(name.clone(), Arc::new(Language::from(name, timeout,lang)));
        }
        new
    }

    pub fn from_toml_file<P>(path: P) -> impl Future<Item = Self, Error = String>
        where P: AsRef<Path> + Send + Display + 'static{
        util::decode(path).map(EvalService::fixup)
    }

    pub fn from_toml(toml: &str) -> Result<Self, String> {
        toml::from_str(toml).map(EvalService::fixup).map_err(|x| format!("could not parse TOML: {:?}", x))
    }

    pub fn langs(&self) -> impl Iterator<Item = (&str, &Arc<Language>)> {
        self.languages.iter().map(|(n, l)| (n.as_str(), l))
    }

    pub fn get(&self, lang: &str) -> Option<&Arc<Language>> {
        self.languages.get(lang)
    }
}

static EMPTY_U8: [u8; 0] = [];

impl Language {
    pub fn eval<T, U>(&self, code: T, timeout: Option<usize>, context: Option<U>) -> impl Future<Item = String, Error = String>
        where T: AsRef<str>, U: AsRef<str> {
        debug!("evaluating {}: \"{}\"", self.name, code.as_ref());
        match self.backend {
            Backend::Exec(ref lang) =>
                Either::A(Either::A(
                    eval::exec(
                        lang.clone(),
                        timeout.or(self.timeout),
                        self.wrap_code(code.as_ref())))),
            Backend::UnixSocket(ref lang) =>
                Either::A(Either::B(
                    eval::unix(
                        lang.clone(),
                        timeout.or(self.timeout),
                        context.map(|x| x.as_ref().to_owned()), // FIXME copy :(
                        self.wrap_code(code.as_ref())))),
            _ => Either::B(futures::finished("Unimplemented".to_owned()))
        }
    }

    fn wrap_code(&self, raw: &str) -> String {
        let mut code = String::with_capacity(raw.len());

        if let Some(ref prefix) = self.code_before {
            code.push_str(prefix);
        }

        code.push_str(raw);

        if let Some(ref postfix) = self.code_after {
            code.push_str(postfix);
        }

        code
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_decode() {
        let toml = r#"
timeout = 20

[languages.rs]
cmdline = ["rustc", "-O"]

[languages.'rs!']
timeout = 0
cmdline = ["rustc", "-O"]
"#;
        println!("{:#?}", super::EvalService::from_toml(toml).unwrap());
    }
}
<commit_msg>Fix overriding with none<commit_after>extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate toml;
extern crate tokio;
extern crate tokio_process;
#[macro_use] extern crate futures;
#[macro_use] extern crate log;
extern crate bytes;

use std::collections::HashMap;
use futures::Future;
use futures::future::Either;
use std::path::Path;
use std::fmt::Display;
use std::sync::Arc;

pub mod util;
mod eval;

#[derive(Clone, Serialize, Deserialize, Default, PartialEq, Debug)]
struct EvalServiceCfg {
    timeout: usize,
    languages: HashMap<String, LanguageCfg>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct LanguageCfg {
    code_before: Option<String>,
    code_after: Option<String>,
    timeout: Option<usize>,
    #[serde(flatten)]
    backend: BackendCfg
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum BackendCfg {
    Exec(ExecBackend),
    Network(NetworkBackend),
    UnixSocket(UnixSocketBackend)
}

#[derive(Clone, Debug)]
pub struct EvalService {
    timeout: usize,
    languages: HashMap<String, Arc<Language>>
}

#[derive(Clone, PartialEq, Debug)]
pub struct Language {
    name: String,
    code_before: Option<String>,
    code_after: Option<String>,
    timeout: Option<usize>,
    backend: Backend
}

#[derive(Clone, PartialEq, Debug)]
enum Backend {
    Exec(Arc<ExecBackend>),
    Network(Arc<NetworkBackend>),
    UnixSocket(Arc<UnixSocketBackend>)
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct ExecBackend {
    cmdline: Vec<String>,
    timeout_prefix: Option<String>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct NetworkBackend {
    network_addr: String,
    timeout_cmdline: Option<Vec<String>>
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
pub struct UnixSocketBackend {
    socket_addr: String,
    timeout_cmdline: Option<Vec<String>>
}

impl Language {
    fn from(name: String, default_timeout: usize, cfg: LanguageCfg) -> Self {
        Language {
            name,
            code_before: cfg.code_before,
            code_after: cfg.code_after,
            timeout: cfg.timeout.or_else(|| Some(default_timeout)),
            backend: match cfg.backend {
                BackendCfg::Exec(x) => Backend::Exec(Arc::new(x)),
                BackendCfg::Network(x) => Backend::Network(Arc::new(x)),
                BackendCfg::UnixSocket(x) => Backend::UnixSocket(Arc::new(x)),
            }
        }
    }
}

impl EvalService {
    fn fixup(cfg: EvalServiceCfg) -> Self {
        debug!("Loaded config: {:#?}", cfg);
        let mut new = EvalService {
            timeout: cfg.timeout,
            languages: HashMap::new()
        };
        let timeout = cfg.timeout;
        for (name, lang) in cfg.languages.into_iter() {
            new.languages.insert(name.clone(), Arc::new(Language::from(name, timeout,lang)));
        }
        new
    }

    pub fn from_toml_file<P>(path: P) -> impl Future<Item = Self, Error = String>
        where P: AsRef<Path> + Send + Display + 'static{
        util::decode(path).map(EvalService::fixup)
    }

    pub fn from_toml(toml: &str) -> Result<Self, String> {
        toml::from_str(toml).map(EvalService::fixup).map_err(|x| format!("could not parse TOML: {:?}", x))
    }

    pub fn langs(&self) -> impl Iterator<Item = (&str, &Arc<Language>)> {
        self.languages.iter().map(|(n, l)| (n.as_str(), l))
    }

    pub fn get(&self, lang: &str) -> Option<&Arc<Language>> {
        self.languages.get(lang)
    }
}

static EMPTY_U8: [u8; 0] = [];

impl Language {
    pub fn eval<T, U>(&self, code: T, timeout: Option<usize>, context: Option<U>) -> impl Future<Item = String, Error = String>
        where T: AsRef<str>, U: AsRef<str> {
        debug!("evaluating {}: \"{}\"", self.name, code.as_ref());
        let timeout = match timeout {
            Some(0) => None,
            Some(n) => Some(n),
            None => self.timeout
        };
        match self.backend {
            Backend::Exec(ref lang) =>
                Either::A(Either::A(
                    eval::exec(
                        lang.clone(),
                        timeout,
                        self.wrap_code(code.as_ref())))),
            Backend::UnixSocket(ref lang) =>
                Either::A(Either::B(
                    eval::unix(
                        lang.clone(),
                        timeout,
                        context.map(|x| x.as_ref().to_owned()), // FIXME copy :(
                        self.wrap_code(code.as_ref())))),
            _ => Either::B(futures::finished("Unimplemented".to_owned()))
        }
    }

    fn wrap_code(&self, raw: &str) -> String {
        let mut code = String::with_capacity(raw.len());

        if let Some(ref prefix) = self.code_before {
            code.push_str(prefix);
        }

        code.push_str(raw);

        if let Some(ref postfix) = self.code_after {
            code.push_str(postfix);
        }

        code
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_decode() {
        let toml = r#"
timeout = 20

[languages.rs]
cmdline = ["rustc", "-O"]

[languages.'rs!']
timeout = 0
cmdline = ["rustc", "-O"]
"#;
        println!("{:#?}", super::EvalService::from_toml(toml).unwrap());
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{collections::LinkedList, iter};

use hir::{Adt, HasSource, Semantics};
use ra_ide_db::RootDatabase;

use crate::{Assist, AssistCtx, AssistId};
use ra_syntax::{
    ast::{self, edit::IndentLevel, make, AstNode, NameOwner},
    SyntaxKind, SyntaxNode,
};

use ast::{MatchArm, MatchGuard, Pat};

// Assist: fill_match_arms
//
// Adds missing clauses to a `match` expression.
//
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         <|>
//     }
// }
// ```
// ->
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         Action::Move { distance } => (),
//         Action::Stop => (),
//     }
// }
// ```
pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
    let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
    let match_arm_list = match_expr.match_arm_list()?;

    let expr = match_expr.expr()?;
    let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
    let module = ctx.sema.scope(expr.syntax()).module()?;

    let variants = enum_def.variants(ctx.db);
    if variants.is_empty() {
        return None;
    }

    let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
    if arms.len() == 1 {
        if let Some(Pat::PlaceholderPat(..)) = arms[0].pat() {
            arms.clear();
        }
    }

    let mut has_partial_match = false;
    let db = ctx.db;
    let missing_arms: Vec<MatchArm> = variants
        .into_iter()
        .filter_map(|variant| build_pat(db, module, variant))
        .filter(|variant_pat| {
            !arms.iter().filter_map(|arm| arm.pat().map(|_| arm)).any(|arm| {
                let pat = arm.pat().unwrap();

                // Special casee OrPat as separate top-level pats
                let pats: Vec<Pat> = match Pat::from(pat.clone()) {
                    Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
                    _ => vec![pat],
                };

                pats.iter().any(|pat| {
                    match does_arm_pat_match_variant(pat, arm.guard(), variant_pat) {
                        ArmMatch::Yes => true,
                        ArmMatch::No => false,
                        ArmMatch::Partial => {
                            has_partial_match = true;
                            true
                        }
                    }
                })
            })
        })
        .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
        .collect();

    if missing_arms.is_empty() && !has_partial_match {
        return None;
    }

    ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
        arms.extend(missing_arms);
        if has_partial_match {
            arms.push(make::match_arm(
                iter::once(make::placeholder_pat().into()),
                make::expr_unit(),
            ));
        }

        let indent_level = IndentLevel::from_node(match_arm_list.syntax());
        let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));

        edit.target(match_expr.syntax().text_range());
        edit.set_cursor(expr.syntax().text_range().start());
        edit.replace_ast(match_arm_list, new_arm_list);
    })
}

enum ArmMatch {
    Yes,
    No,
    Partial,
}

fn does_arm_pat_match_variant(arm: &Pat, arm_guard: Option<MatchGuard>, var: &Pat) -> ArmMatch {
    let arm = flatten_pats(arm.clone());
    let var = flatten_pats(var.clone());
    let mut arm = arm.iter();
    let mut var = var.iter();

    // If the first part of the Pat don't match, there's no match
    match (arm.next(), var.next()) {
        (Some(arm), Some(var)) if arm.text() == var.text() => {}
        _ => return ArmMatch::No,
    }

    // If we have a guard we automatically know we have a partial match
    if arm_guard.is_some() {
        return ArmMatch::Partial;
    }

    if arm.clone().count() != var.clone().count() {
        return ArmMatch::Partial;
    }

    let direct_match = arm.zip(var).all(|(arm, var)| {
        if arm.text() == var.text() {
            return true;
        }
        match (arm.kind(), var.kind()) {
            (SyntaxKind::PLACEHOLDER_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
            (SyntaxKind::DOT_DOT_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
            (SyntaxKind::BIND_PAT, SyntaxKind::PLACEHOLDER_PAT) => true,
            _ => false,
        }
    });

    match direct_match {
        true => ArmMatch::Yes,
        false => ArmMatch::Partial,
    }
}

fn flatten_pats(pat: Pat) -> Vec<SyntaxNode> {
    let mut pats: LinkedList<SyntaxNode> = pat.syntax().children().collect();
    let mut out: Vec<SyntaxNode> = vec![];
    while let Some(p) = pats.pop_front() {
        pats.extend(p.children());
        out.push(p);
    }
    out
}

fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
    sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
        Some(Adt::Enum(e)) => Some(e),
        _ => None,
    })
}

fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
    let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);

    // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
    let pat: ast::Pat = match var.source(db).value.kind() {
        ast::StructKind::Tuple(field_list) => {
            let pats =
                iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count());
            make::tuple_struct_pat(path, pats).into()
        }
        ast::StructKind::Record(field_list) => {
            let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into());
            make::record_pat(path, pats).into()
        }
        ast::StructKind::Unit => make::path_pat(path),
    };

    Some(pat)
}

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

    use super::fill_match_arms;

    #[test]
    fn partial_fill_multi() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs(i32, Option<i32>)
            }
            fn main() {
                match A::As<|> {
                    A::Bs(_, Some(_)) => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs(i32, Option<i32>)
            }
            fn main() {
                match <|>A::As {
                    A::Bs(_, Some(_)) => (),
                    A::As => (),
                    _ => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill_record() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs{x:i32, y:Option<i32>},
            }
            fn main() {
                match A::As<|> {
                    A::Bs{x,y:Some(_)} => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs{x:i32, y:Option<i32>},
            }
            fn main() {
                match <|>A::As {
                    A::Bs{x,y:Some(_)} => (),
                    A::As => (),
                    _ => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill_or_pat() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match A::As<|> {
                    A::Cs(_) | A::Bs => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match <|>A::As {
                    A::Cs(_) | A::Bs => (),
                    A::As => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill_or_pat2() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match A::As<|> {
                    A::Cs(Some(_)) | A::Bs => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match <|>A::As {
                    A::Cs(Some(_)) | A::Bs => (),
                    A::As => (),
                    _ => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs,
                Ds(String),
                Es(B),
            }
            enum B {
                Xs,
                Ys,
            }
            fn main() {
                match A::As<|> {
                    A::Bs if 0 < 1 => (),
                    A::Ds(_value) => (),
                    A::Es(B::Xs) => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs,
                Ds(String),
                Es(B),
            }
            enum B {
                Xs,
                Ys,
            }
            fn main() {
                match <|>A::As {
                    A::Bs if 0 < 1 => (),
                    A::Ds(_value) => (),
                    A::Es(B::Xs) => (),
                    A::As => (),
                    A::Cs => (),
                    _ => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_empty_body() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs(String),
                Ds(String, String),
                Es{ x: usize, y: usize }
            }

            fn main() {
                let a = A::As;
                match a<|> {}
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs(String),
                Ds(String, String),
                Es{ x: usize, y: usize }
            }

            fn main() {
                let a = A::As;
                match <|>a {
                    A::As => (),
                    A::Bs => (),
                    A::Cs(_) => (),
                    A::Ds(_, _) => (),
                    A::Es { x, y } => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn test_fill_match_arm_refs() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
            }

            fn foo(a: &A) {
                match a<|> {
                }
            }
            "#,
            r#"
            enum A {
                As,
            }

            fn foo(a: &A) {
                match <|>a {
                    A::As => (),
                }
            }
            "#,
        );

        check_assist(
            fill_match_arms,
            r#"
            enum A {
                Es{ x: usize, y: usize }
            }

            fn foo(a: &mut A) {
                match a<|> {
                }
            }
            "#,
            r#"
            enum A {
                Es{ x: usize, y: usize }
            }

            fn foo(a: &mut A) {
                match <|>a {
                    A::Es { x, y } => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_target() {
        check_assist_target(
            fill_match_arms,
            r#"
            enum E { X, Y }

            fn main() {
                match E::X<|> {}
            }
            "#,
            "match E::X {}",
        );
    }

    #[test]
    fn fill_match_arms_trivial_arm() {
        check_assist(
            fill_match_arms,
            r#"
            enum E { X, Y }

            fn main() {
                match E::X {
                    <|>_ => {},
                }
            }
            "#,
            r#"
            enum E { X, Y }

            fn main() {
                match <|>E::X {
                    E::X => (),
                    E::Y => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_qualifies_path() {
        check_assist(
            fill_match_arms,
            r#"
            mod foo { pub enum E { X, Y } }
            use foo::E::X;

            fn main() {
                match X {
                    <|>
                }
            }
            "#,
            r#"
            mod foo { pub enum E { X, Y } }
            use foo::E::X;

            fn main() {
                match <|>X {
                    X => (),
                    foo::E::Y => (),
                }
            }
            "#,
        );
    }
}
<commit_msg>Address nits and suggestions.<commit_after>//! FIXME: write short doc here

use std::iter;

use hir::{Adt, HasSource, Semantics};
use ra_ide_db::RootDatabase;

use crate::{Assist, AssistCtx, AssistId};
use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};

use ast::{MatchArm, Pat};

// Assist: fill_match_arms
//
// Adds missing clauses to a `match` expression.
//
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         <|>
//     }
// }
// ```
// ->
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         Action::Move { distance } => (),
//         Action::Stop => (),
//     }
// }
// ```
pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
    let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
    let match_arm_list = match_expr.match_arm_list()?;

    let expr = match_expr.expr()?;
    let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
    let module = ctx.sema.scope(expr.syntax()).module()?;

    let variants = enum_def.variants(ctx.db);
    if variants.is_empty() {
        return None;
    }

    let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
    if arms.len() == 1 {
        if let Some(Pat::PlaceholderPat(..)) = arms[0].pat() {
            arms.clear();
        }
    }

    let db = ctx.db;
    let missing_arms: Vec<MatchArm> = variants
        .into_iter()
        .filter_map(|variant| build_pat(db, module, variant))
        .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
        .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
        .collect();

    if missing_arms.is_empty() {
        return None;
    }

    ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
        arms.extend(missing_arms);

        let indent_level = IndentLevel::from_node(match_arm_list.syntax());
        let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));

        edit.target(match_expr.syntax().text_range());
        edit.set_cursor(expr.syntax().text_range().start());
        edit.replace_ast(match_arm_list, new_arm_list);
    })
}

fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
    existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| {
        // Special casee OrPat as separate top-level pats
        let top_level_pats: Vec<Pat> = match pat {
            Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
            _ => vec![pat],
        };

        !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
    })
}

fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
    let pat_head = pat.syntax().first_child().map(|node| node.text());
    let var_head = var.syntax().first_child().map(|node| node.text());

    pat_head == var_head
}

fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
    sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
        Some(Adt::Enum(e)) => Some(e),
        _ => None,
    })
}

fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
    let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);

    // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
    let pat: ast::Pat = match var.source(db).value.kind() {
        ast::StructKind::Tuple(field_list) => {
            let pats =
                iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count());
            make::tuple_struct_pat(path, pats).into()
        }
        ast::StructKind::Record(field_list) => {
            let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into());
            make::record_pat(path, pats).into()
        }
        ast::StructKind::Unit => make::path_pat(path),
    };

    Some(pat)
}

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

    use super::fill_match_arms;

    #[test]
    fn all_match_arms_provided() {
        check_assist_not_applicable(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs{x:i32, y:Option<i32>},
                Cs(i32, Option<i32>),
            }
            fn main() {
                match A::As<|> {
                    A::As,
                    A::Bs{x,y:Some(_)} => (),
                    A::Cs(_, Some(_)) => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill_record_tuple() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs{x:i32, y:Option<i32>},
                Cs(i32, Option<i32>),
            }
            fn main() {
                match A::As<|> {
                    A::Bs{x,y:Some(_)} => (),
                    A::Cs(_, Some(_)) => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs{x:i32, y:Option<i32>},
                Cs(i32, Option<i32>),
            }
            fn main() {
                match <|>A::As {
                    A::Bs{x,y:Some(_)} => (),
                    A::Cs(_, Some(_)) => (),
                    A::As => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill_or_pat() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match A::As<|> {
                    A::Cs(_) | A::Bs => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs(Option<i32>),
            }
            fn main() {
                match <|>A::As {
                    A::Cs(_) | A::Bs => (),
                    A::As => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn partial_fill() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs,
                Ds(String),
                Es(B),
            }
            enum B {
                Xs,
                Ys,
            }
            fn main() {
                match A::As<|> {
                    A::Bs if 0 < 1 => (),
                    A::Ds(_value) => (),
                    A::Es(B::Xs) => (),
                }
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs,
                Ds(String),
                Es(B),
            }
            enum B {
                Xs,
                Ys,
            }
            fn main() {
                match <|>A::As {
                    A::Bs if 0 < 1 => (),
                    A::Ds(_value) => (),
                    A::Es(B::Xs) => (),
                    A::As => (),
                    A::Cs => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_empty_body() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
                Bs,
                Cs(String),
                Ds(String, String),
                Es{ x: usize, y: usize }
            }

            fn main() {
                let a = A::As;
                match a<|> {}
            }
            "#,
            r#"
            enum A {
                As,
                Bs,
                Cs(String),
                Ds(String, String),
                Es{ x: usize, y: usize }
            }

            fn main() {
                let a = A::As;
                match <|>a {
                    A::As => (),
                    A::Bs => (),
                    A::Cs(_) => (),
                    A::Ds(_, _) => (),
                    A::Es { x, y } => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn test_fill_match_arm_refs() {
        check_assist(
            fill_match_arms,
            r#"
            enum A {
                As,
            }

            fn foo(a: &A) {
                match a<|> {
                }
            }
            "#,
            r#"
            enum A {
                As,
            }

            fn foo(a: &A) {
                match <|>a {
                    A::As => (),
                }
            }
            "#,
        );

        check_assist(
            fill_match_arms,
            r#"
            enum A {
                Es{ x: usize, y: usize }
            }

            fn foo(a: &mut A) {
                match a<|> {
                }
            }
            "#,
            r#"
            enum A {
                Es{ x: usize, y: usize }
            }

            fn foo(a: &mut A) {
                match <|>a {
                    A::Es { x, y } => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_target() {
        check_assist_target(
            fill_match_arms,
            r#"
            enum E { X, Y }

            fn main() {
                match E::X<|> {}
            }
            "#,
            "match E::X {}",
        );
    }

    #[test]
    fn fill_match_arms_trivial_arm() {
        check_assist(
            fill_match_arms,
            r#"
            enum E { X, Y }

            fn main() {
                match E::X {
                    <|>_ => {},
                }
            }
            "#,
            r#"
            enum E { X, Y }

            fn main() {
                match <|>E::X {
                    E::X => (),
                    E::Y => (),
                }
            }
            "#,
        );
    }

    #[test]
    fn fill_match_arms_qualifies_path() {
        check_assist(
            fill_match_arms,
            r#"
            mod foo { pub enum E { X, Y } }
            use foo::E::X;

            fn main() {
                match X {
                    <|>
                }
            }
            "#,
            r#"
            mod foo { pub enum E { X, Y } }
            use foo::E::X;

            fn main() {
                match <|>X {
                    X => (),
                    foo::E::Y => (),
                }
            }
            "#,
        );
    }
}
<|endoftext|>