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

extern crate ctrlc;

use exec;
use fshelper;
use internal::{print_error, print_error_and_exit, FdOptions, EXITCODE_SIGINT, MAX_BUFFER_LENGTH};
use output;

use std::error::Error;
use std::path::PathBuf;
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;

use ignore::overrides::OverrideBuilder;
use ignore::{self, WalkBuilder};
use regex::Regex;

/// The receiver thread can either be buffering results or directly streaming to the console.
enum ReceiverMode {
    /// Receiver is still buffering in order to sort the results, if the search finishes fast
    /// enough.
    Buffering,

    /// Receiver is directly printing results to the output.
    Streaming,
}

/// The Worker threads can result in a valid entry having PathBuf or an error.
pub enum WorkerResult {
    Entry(PathBuf),
    Error(ignore::Error),
}

/// Recursively scan the given search path for files / pathnames matching the pattern.
///
/// If the `--exec` argument was supplied, this will create a thread pool for executing
/// jobs in parallel from a given command line and the discovered paths. Otherwise, each
/// path will simply be written to standard output.
pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<FdOptions>) {
    let mut path_iter = path_vec.iter();
    let first_path_buf = path_iter
        .next()
        .expect("Error: Path vector can not be empty");
    let (tx, rx) = channel();
    let threads = config.threads;

    let mut override_builder = OverrideBuilder::new(first_path_buf.as_path());

    for pattern in &config.exclude_patterns {
        let res = override_builder.add(pattern);
        if res.is_err() {
            print_error_and_exit(&format!("Error: malformed exclude pattern '{}'", pattern));
        }
    }
    let overrides = override_builder.build().unwrap_or_else(|_| {
        print_error_and_exit("Mismatch in exclude patterns");
    });

    let mut walker = WalkBuilder::new(first_path_buf.as_path());
    walker
        .hidden(config.ignore_hidden)
        .ignore(false)
        .parents(config.read_fdignore || config.read_vcsignore)
        .git_ignore(config.read_vcsignore)
        .git_global(config.read_vcsignore)
        .git_exclude(config.read_vcsignore)
        .overrides(overrides)
        .follow_links(config.follow_links)
        .max_depth(config.max_depth);

    if config.read_fdignore {
        walker.add_custom_ignore_filename(".fdignore");
    }

    for ignore_file in &config.ignore_files {
        let result = walker.add_ignore(ignore_file);
        if let Some(err) = result {
            match err {
                ignore::Error::Partial(_) => (),
                _ => {
                    print_error_and_exit(&format!(
                        "Error while parsing custom ignore file '{}': {}.",
                        ignore_file.to_string_lossy(),
                        err.description()
                    ));
                }
            }
        }
    }

    for path_entry in path_iter {
        walker.add(path_entry.as_path());
    }

    let parallel_walker = walker.threads(threads).build_parallel();

    let wants_to_quit = Arc::new(AtomicBool::new(false));
    let receiver_wtq = Arc::clone(&wants_to_quit);
    let sender_wtq = Arc::clone(&wants_to_quit);
    if config.ls_colors.is_some() && config.command.is_none() {
        let wq = Arc::clone(&receiver_wtq);
        ctrlc::set_handler(move || {
            wq.store(true, Ordering::Relaxed);
        })
        .unwrap();
    }

    // Spawn the thread that receives all results through the channel.
    let rx_config = Arc::clone(&config);
    let receiver_thread = thread::spawn(move || {
        // This will be set to `Some` if the `--exec` argument was supplied.
        if let Some(ref cmd) = rx_config.command {
            let shared_rx = Arc::new(Mutex::new(rx));

            let out_perm = Arc::new(Mutex::new(()));

            // TODO: the following line is a workaround to replace the `unsafe` block that was
            // previously used here to avoid the (unnecessary?) cloning of the command. The
            // `unsafe` block caused problems on some platforms (SIGILL instructions on Linux) and
            // therefore had to be removed.
            let cmd = Arc::new(cmd.clone());

            // Each spawned job will store it's thread handle in here.
            let mut handles = Vec::with_capacity(threads);
            for _ in 0..threads {
                let rx = Arc::clone(&shared_rx);
                let cmd = Arc::clone(&cmd);
                let out_perm = Arc::clone(&out_perm);

                // Spawn a job thread that will listen for and execute inputs.
                let handle = thread::spawn(move || exec::job(rx, cmd, out_perm));

                // Push the handle of the spawned thread into the vector for later joining.
                handles.push(handle);
            }

            // Wait for all threads to exit before exiting the program.
            for h in handles {
                h.join().unwrap();
            }
        } else {
            let start = time::Instant::now();

            let mut buffer = vec![];

            // Start in buffering mode
            let mut mode = ReceiverMode::Buffering;

            // Maximum time to wait before we start streaming to the console.
            let max_buffer_time = rx_config
                .max_buffer_time
                .unwrap_or_else(|| time::Duration::from_millis(100));

            for worker_result in rx {
                match worker_result {
                    WorkerResult::Entry(value) => {
                        match mode {
                            ReceiverMode::Buffering => {
                                buffer.push(value);

                                // Have we reached the maximum buffer size or maximum buffering time?
                                if buffer.len() > MAX_BUFFER_LENGTH
                                    || time::Instant::now() - start > max_buffer_time
                                {
                                    // Flush the buffer
                                    for v in &buffer {
                                        output::print_entry(v, &rx_config, &receiver_wtq);
                                    }
                                    buffer.clear();

                                    // Start streaming
                                    mode = ReceiverMode::Streaming;
                                }
                            }
                            ReceiverMode::Streaming => {
                                output::print_entry(&value, &rx_config, &receiver_wtq);
                            }
                        }
                    }
                    WorkerResult::Error(err) => {
                        print_error(&format!("{}", err));
                    }
                }
            }

            // If we have finished fast enough (faster than max_buffer_time), we haven't streamed
            // anything to the console, yet. In this case, sort the results and print them:
            if !buffer.is_empty() {
                buffer.sort();
                for value in buffer {
                    output::print_entry(&value, &rx_config, &receiver_wtq);
                }
            }
        }
    });

    // Spawn the sender threads.
    parallel_walker.run(|| {
        let config = Arc::clone(&config);
        let pattern = Arc::clone(&pattern);
        let tx_thread = tx.clone();
        let wants_to_quit = Arc::clone(&sender_wtq);

        Box::new(move |entry_o| {
            if wants_to_quit.load(Ordering::Relaxed) {
                return ignore::WalkState::Quit;
            }

            let entry = match entry_o {
                Ok(e) => e,
                Err(err) => {
                    tx_thread.send(WorkerResult::Error(err)).unwrap();
                    return ignore::WalkState::Continue;
                }
            };

            if entry.depth() == 0 {
                return ignore::WalkState::Continue;
            }

            // Filter out unwanted file types.

            if let Some(ref file_types) = config.file_types {
                if let Some(ref entry_type) = entry.file_type() {
                    if (!file_types.files && entry_type.is_file())
                        || (!file_types.directories && entry_type.is_dir())
                        || (!file_types.symlinks && entry_type.is_symlink())
                        || (file_types.executables_only && !entry
                            .metadata()
                            .map(|m| fshelper::is_executable(&m))
                            .unwrap_or(false))
                        || (file_types.empty_only && !fshelper::is_empty(&entry))
                    {
                        return ignore::WalkState::Continue;
                    } else if !(entry_type.is_file()
                        || entry_type.is_dir()
                        || entry_type.is_symlink())
                    {
                        // This is probably a block device, char device, fifo or socket. Skip it.
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            let entry_path = entry.path();

            // Filter out unwanted extensions.
            if let Some(ref exts_regex) = config.extensions {
                if let Some(path_str) = entry_path.file_name().map_or(None, |s| s.to_str()) {
                    if !exts_regex.is_match(path_str) {
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            // Filter out unwanted sizes if it is a file and we have been given size constraints.
            if config.size_constraints.len() > 0 {
                if entry_path.is_file() {
                    if let Ok(metadata) = entry_path.metadata() {
                        let file_size = metadata.len();
                        if config
                            .size_constraints
                            .iter()
                            .any(|sc| !sc.is_within(file_size))
                        {
                            return ignore::WalkState::Continue;
                        }
                    } else {
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            let search_str_o = if config.search_full_path {
                match fshelper::path_absolute_form(entry_path) {
                    Ok(path_abs_buf) => Some(path_abs_buf.to_string_lossy().into_owned().into()),
                    Err(_) => print_error_and_exit("Error: unable to get full path."),
                }
            } else {
                entry_path.file_name().map(|f| f.to_string_lossy())
            };

            if let Some(search_str) = search_str_o {
                if pattern.is_match(&*search_str) {
                    // TODO: take care of the unwrap call
                    tx_thread
                        .send(WorkerResult::Entry(entry_path.to_owned()))
                        .unwrap()
                }
            }

            ignore::WalkState::Continue
        })
    });

    // Drop the initial sender. If we don't do this, the receiver will block even
    // if all threads have finished, since there is still one sender around.
    drop(tx);

    // Wait for the receiver thread to print out all results.
    receiver_thread.join().unwrap();

    if wants_to_quit.load(Ordering::Relaxed) {
        process::exit(EXITCODE_SIGINT);
    }
}
<commit_msg>refactor: loosen strict handling of missing --ignore-file<commit_after>// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

extern crate ctrlc;

use exec;
use fshelper;
use internal::{print_error, print_error_and_exit, FdOptions, EXITCODE_SIGINT, MAX_BUFFER_LENGTH};
use output;

use std::error::Error;
use std::path::PathBuf;
use std::process;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;

use ignore::overrides::OverrideBuilder;
use ignore::{self, WalkBuilder};
use regex::Regex;

/// The receiver thread can either be buffering results or directly streaming to the console.
enum ReceiverMode {
    /// Receiver is still buffering in order to sort the results, if the search finishes fast
    /// enough.
    Buffering,

    /// Receiver is directly printing results to the output.
    Streaming,
}

/// The Worker threads can result in a valid entry having PathBuf or an error.
pub enum WorkerResult {
    Entry(PathBuf),
    Error(ignore::Error),
}

/// Recursively scan the given search path for files / pathnames matching the pattern.
///
/// If the `--exec` argument was supplied, this will create a thread pool for executing
/// jobs in parallel from a given command line and the discovered paths. Otherwise, each
/// path will simply be written to standard output.
pub fn scan(path_vec: &[PathBuf], pattern: Arc<Regex>, config: Arc<FdOptions>) {
    let mut path_iter = path_vec.iter();
    let first_path_buf = path_iter
        .next()
        .expect("Error: Path vector can not be empty");
    let (tx, rx) = channel();
    let threads = config.threads;

    let mut override_builder = OverrideBuilder::new(first_path_buf.as_path());

    for pattern in &config.exclude_patterns {
        let res = override_builder.add(pattern);
        if res.is_err() {
            print_error_and_exit(&format!("Error: malformed exclude pattern '{}'", pattern));
        }
    }
    let overrides = override_builder.build().unwrap_or_else(|_| {
        print_error_and_exit("Mismatch in exclude patterns");
    });

    let mut walker = WalkBuilder::new(first_path_buf.as_path());
    walker
        .hidden(config.ignore_hidden)
        .ignore(false)
        .parents(config.read_fdignore || config.read_vcsignore)
        .git_ignore(config.read_vcsignore)
        .git_global(config.read_vcsignore)
        .git_exclude(config.read_vcsignore)
        .overrides(overrides)
        .follow_links(config.follow_links)
        .max_depth(config.max_depth);

    if config.read_fdignore {
        walker.add_custom_ignore_filename(".fdignore");
    }

    for ignore_file in &config.ignore_files {
        let result = walker.add_ignore(ignore_file);
        if let Some(err) = result {
            match err {
                ignore::Error::Partial(_) => (),
                _ => {
                    print_error(&format!(
                        "Error while parsing custom ignore file '{}': {}.",
                        ignore_file.to_string_lossy(),
                        err.description()
                    ));
                }
            }
        }
    }

    for path_entry in path_iter {
        walker.add(path_entry.as_path());
    }

    let parallel_walker = walker.threads(threads).build_parallel();

    let wants_to_quit = Arc::new(AtomicBool::new(false));
    let receiver_wtq = Arc::clone(&wants_to_quit);
    let sender_wtq = Arc::clone(&wants_to_quit);
    if config.ls_colors.is_some() && config.command.is_none() {
        let wq = Arc::clone(&receiver_wtq);
        ctrlc::set_handler(move || {
            wq.store(true, Ordering::Relaxed);
        })
        .unwrap();
    }

    // Spawn the thread that receives all results through the channel.
    let rx_config = Arc::clone(&config);
    let receiver_thread = thread::spawn(move || {
        // This will be set to `Some` if the `--exec` argument was supplied.
        if let Some(ref cmd) = rx_config.command {
            let shared_rx = Arc::new(Mutex::new(rx));

            let out_perm = Arc::new(Mutex::new(()));

            // TODO: the following line is a workaround to replace the `unsafe` block that was
            // previously used here to avoid the (unnecessary?) cloning of the command. The
            // `unsafe` block caused problems on some platforms (SIGILL instructions on Linux) and
            // therefore had to be removed.
            let cmd = Arc::new(cmd.clone());

            // Each spawned job will store it's thread handle in here.
            let mut handles = Vec::with_capacity(threads);
            for _ in 0..threads {
                let rx = Arc::clone(&shared_rx);
                let cmd = Arc::clone(&cmd);
                let out_perm = Arc::clone(&out_perm);

                // Spawn a job thread that will listen for and execute inputs.
                let handle = thread::spawn(move || exec::job(rx, cmd, out_perm));

                // Push the handle of the spawned thread into the vector for later joining.
                handles.push(handle);
            }

            // Wait for all threads to exit before exiting the program.
            for h in handles {
                h.join().unwrap();
            }
        } else {
            let start = time::Instant::now();

            let mut buffer = vec![];

            // Start in buffering mode
            let mut mode = ReceiverMode::Buffering;

            // Maximum time to wait before we start streaming to the console.
            let max_buffer_time = rx_config
                .max_buffer_time
                .unwrap_or_else(|| time::Duration::from_millis(100));

            for worker_result in rx {
                match worker_result {
                    WorkerResult::Entry(value) => {
                        match mode {
                            ReceiverMode::Buffering => {
                                buffer.push(value);

                                // Have we reached the maximum buffer size or maximum buffering time?
                                if buffer.len() > MAX_BUFFER_LENGTH
                                    || time::Instant::now() - start > max_buffer_time
                                {
                                    // Flush the buffer
                                    for v in &buffer {
                                        output::print_entry(v, &rx_config, &receiver_wtq);
                                    }
                                    buffer.clear();

                                    // Start streaming
                                    mode = ReceiverMode::Streaming;
                                }
                            }
                            ReceiverMode::Streaming => {
                                output::print_entry(&value, &rx_config, &receiver_wtq);
                            }
                        }
                    }
                    WorkerResult::Error(err) => {
                        print_error(&format!("{}", err));
                    }
                }
            }

            // If we have finished fast enough (faster than max_buffer_time), we haven't streamed
            // anything to the console, yet. In this case, sort the results and print them:
            if !buffer.is_empty() {
                buffer.sort();
                for value in buffer {
                    output::print_entry(&value, &rx_config, &receiver_wtq);
                }
            }
        }
    });

    // Spawn the sender threads.
    parallel_walker.run(|| {
        let config = Arc::clone(&config);
        let pattern = Arc::clone(&pattern);
        let tx_thread = tx.clone();
        let wants_to_quit = Arc::clone(&sender_wtq);

        Box::new(move |entry_o| {
            if wants_to_quit.load(Ordering::Relaxed) {
                return ignore::WalkState::Quit;
            }

            let entry = match entry_o {
                Ok(e) => e,
                Err(err) => {
                    tx_thread.send(WorkerResult::Error(err)).unwrap();
                    return ignore::WalkState::Continue;
                }
            };

            if entry.depth() == 0 {
                return ignore::WalkState::Continue;
            }

            // Filter out unwanted file types.

            if let Some(ref file_types) = config.file_types {
                if let Some(ref entry_type) = entry.file_type() {
                    if (!file_types.files && entry_type.is_file())
                        || (!file_types.directories && entry_type.is_dir())
                        || (!file_types.symlinks && entry_type.is_symlink())
                        || (file_types.executables_only && !entry
                            .metadata()
                            .map(|m| fshelper::is_executable(&m))
                            .unwrap_or(false))
                        || (file_types.empty_only && !fshelper::is_empty(&entry))
                    {
                        return ignore::WalkState::Continue;
                    } else if !(entry_type.is_file()
                        || entry_type.is_dir()
                        || entry_type.is_symlink())
                    {
                        // This is probably a block device, char device, fifo or socket. Skip it.
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            let entry_path = entry.path();

            // Filter out unwanted extensions.
            if let Some(ref exts_regex) = config.extensions {
                if let Some(path_str) = entry_path.file_name().map_or(None, |s| s.to_str()) {
                    if !exts_regex.is_match(path_str) {
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            // Filter out unwanted sizes if it is a file and we have been given size constraints.
            if config.size_constraints.len() > 0 {
                if entry_path.is_file() {
                    if let Ok(metadata) = entry_path.metadata() {
                        let file_size = metadata.len();
                        if config
                            .size_constraints
                            .iter()
                            .any(|sc| !sc.is_within(file_size))
                        {
                            return ignore::WalkState::Continue;
                        }
                    } else {
                        return ignore::WalkState::Continue;
                    }
                } else {
                    return ignore::WalkState::Continue;
                }
            }

            let search_str_o = if config.search_full_path {
                match fshelper::path_absolute_form(entry_path) {
                    Ok(path_abs_buf) => Some(path_abs_buf.to_string_lossy().into_owned().into()),
                    Err(_) => print_error_and_exit("Error: unable to get full path."),
                }
            } else {
                entry_path.file_name().map(|f| f.to_string_lossy())
            };

            if let Some(search_str) = search_str_o {
                if pattern.is_match(&*search_str) {
                    // TODO: take care of the unwrap call
                    tx_thread
                        .send(WorkerResult::Entry(entry_path.to_owned()))
                        .unwrap()
                }
            }

            ignore::WalkState::Continue
        })
    });

    // Drop the initial sender. If we don't do this, the receiver will block even
    // if all threads have finished, since there is still one sender around.
    drop(tx);

    // Wait for the receiver thread to print out all results.
    receiver_thread.join().unwrap();

    if wants_to_quit.load(Ordering::Relaxed) {
        process::exit(EXITCODE_SIGINT);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate prodbg_api;
extern crate gdb_remote;
use prodbg_api::*;
use std::os::raw::{c_void};
use gdb_remote::GdbRemote;

struct AmigaUaeBackend {
    capstone: Capstone,
    conn: GdbRemote,
    exception_location: u32,
}

impl AmigaUaeBackend {
    fn get_u32(data: &[u8]) -> u32 {
        let t0 = data[0] as u32;
        let t1 = data[1] as u32;
        let t2 = data[2] as u32;
        let t3 = data[3] as u32;
        (t0 << 24) | (t1 << 16) | (t2 << 8) | t3
    }

    fn write_register(writer: &mut Writer, name: &str, data: u32, read_only: bool) {
        writer.array_entry_begin();
        writer.write_string("name", name);
        writer.write_u8("size", 4);

        if read_only {
            writer.write_u8("read_only", 1);
        }

        writer.write_u32("register", data);

        writer.array_entry_end();
    }

    fn write_registers(&mut self, writer: &mut Writer, data: &[u8]) {
        let mut index = 0;

        writer.event_begin(EventType::SetRegisters as u16);
        writer.array_begin("registers");

        // d registers

        for i in 0..8 {
            let name = format!("d{}", i);
            let reg = Self::get_u32(&data[index..]);
            Self::write_register(writer, &name, reg, false);
            index += 4;
        }

        // a registers

        for i in 0..8 {
            let name = format!("a{}", i);
            let reg = Self::get_u32(&data[index..]);
            Self::write_register(writer, &name, reg, false);
            index += 4;
        }

        // Status registers & pc

        let sr = Self::get_u32(&data[index..]);
        let pc = Self::get_u32(&data[index + 4..]);

        self.exception_location = pc;

        Self::write_register(writer, "sr", sr, true);
        Self::write_register(writer, "pc", pc, true);

        writer.array_end();
        writer.event_end();
    }

    fn write_disassembly(&mut self, reader: &mut Reader, writer: &mut Writer) {
        match self.capstone.open(Arch::M68K, CS_MODE_M68K_000) {
            Err(e) => {
                println!("Unable to open Capstone {}", e as i32);
                return;
            }
            _ => (),
        }

        let mut scratch_string = String::with_capacity(256);

        let address = reader.find_u64("address_start").ok().unwrap();
        let count = reader.find_u32("instruction_count").ok().unwrap();

        let mut data = Vec::<u8>::with_capacity(256 * 1024);
        let memory_fetch_size = count * 4;

        if self.conn.get_memory(&mut data, address, memory_fetch_size as u64).is_err() {
            println!("Unable to fetch memory from {:x} - size {}", address, memory_fetch_size);
            return;
        }

        println!("disasm data address {:x} - len {}", address, data.len());

        if let Ok(insns) = self.capstone.disasm(&data, address as u64, 0) {
            writer.event_begin(EventType::SetDisassembly as u16);
            writer.array_begin("disassembly");
            let mut c = 0;

            for i in insns.iter() {
                let text = format!("{0: <10} {1: <10}", i.mnemonic().unwrap(), i.op_str().unwrap_or(""));
                writer.array_entry_begin();
                writer.write_u32("address", i.address as u32);
                writer.write_string("line", &text);

                scratch_string.clear();

                for register in i.regs_read().unwrap() {
                    let reg_name = self.capstone.reg_name(*register);
                    scratch_string.push_str(reg_name);
                    scratch_string.push(' ');
                }

                if scratch_string.len() > 0 {
                    let t = scratch_string.trim_right();
                    writer.write_string("registers_read", t);
                }

                scratch_string.clear();

                for register in i.regs_write().unwrap() {
                    let reg_name = self.capstone.reg_name(*register);
                    scratch_string.push_str(reg_name);
                    scratch_string.push(' ');
                }

                if scratch_string.len() > 0 {
                    let t = scratch_string.trim_right();
                    writer.write_string("registers_write", t);
                }

                writer.array_entry_end();

                c += 1;
            }

            println!("reported {} instructions", c);

            writer.array_end();
            writer.event_end();
        } else {
            println!("No instructions :(");
        }
    }

    fn get_memory(&mut self, reader: &mut Reader, writer: &mut Writer) {
        let mut data = Vec::<u8>::with_capacity(256 * 1024);

        let address = reader.find_u64("address_start").ok().unwrap();
        let size = reader.find_u32("size").ok().unwrap();

        if self.conn.get_memory(&mut data, address, size as u64).is_err() {
            println!("Unable to fetch memory from {:x} - size {}", address, size);
            return;
        }

        writer.event_begin(EventType::SetMemory as u16);
        writer.write_u64("address", address);
        writer.write_data("data", &data);
        writer.event_end();
    }

    //
    fn write_exception_location(&mut self, writer: &mut Writer) {
        writer.event_begin(EventType::SetExceptionLocation as u16);
        writer.write_u64("address", self.exception_location as u64);
        writer.write_u8("size", 4);
        writer.event_end();
    }

    fn set_breakpoint(&mut self, reader: &mut Reader, _writer: &mut Writer) {
       if let Some(address) = reader.find_u64("address").ok() {
           if self.conn.set_breakpoint_at_address(address).is_err() {
               println!("Unable to set breakpoint at 0x{:08x}", address);
           }
       }
    }

    fn delete_breakpoint(&mut self, reader: &mut Reader, _writer: &mut Writer) {
       if let Some(address) = reader.find_u64("address").ok() {
           if self.conn.remove_breakpoint_at_address(address).is_err() {
               println!("Unable to remove breakpoint at 0x{:08x}", address);
           }
       }
    }

    fn get_registers(&mut self, writer: &mut Writer) {
        let mut register_data = [0; 1024];
        if self.conn.get_registers(&mut register_data).is_ok() {
            println!("setting registers!");
            self.write_registers(writer, &register_data);
        }

        self.write_exception_location(writer);
    }

    fn update_conn_incoming(&mut self, writer: &mut Writer) {
        let mut should_break = false;

        if self.conn.is_connected() {
            if let Some(ref event) = self.conn.read_incoming_event() {
                println!("Got incoming {:?}", event.data);
                // Check if we got an exception back
                if let Some(ref _data) = event.begins_with("S") {
                    println!("Should break");
                    should_break = true;
                }
            }
        }

        if should_break {
            self.get_registers(writer);
        }
    }
}

impl Backend for AmigaUaeBackend {
    fn new(service: &Service) -> Self {
        AmigaUaeBackend {
            capstone: service.get_capstone(),
            conn: GdbRemote::new(),
            exception_location: 0,
        }
    }

    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer) {
        self.update_conn_incoming(writer);

        for event in reader.get_event() {
            match event {
                EVENT_MENU_EVENT => {
                    if self.conn.connect("127.0.0.1:6860").is_ok() {
                        if self.conn.request_no_ack_mode().is_ok() {
                            println!("Connected. Ready to go!");
                        } else {
                            println!("no ack request failed");
                        }
                    } else {
                        println!("Unable to connect to UAE");
                    }
                }
                EVENT_GET_DISASSEMBLY => {
                    self.write_disassembly(reader, writer);
                }

                EVENT_GET_MEMORY => {
                    self.get_memory(reader, writer);
                }

                EVENT_SET_BREAKPOINT => {
                    self.set_breakpoint(reader, writer);
                }

                EVENT_DELETE_BREAKPOINT => {
                    self.delete_breakpoint(reader, writer);
                }

                EVENT_GET_EXCEPTION_LOCATION => {
                    self.write_exception_location(writer);
                }

                _ => (),
            }
        }

        match action {
            ACTION_BREAK => {
                println!("Break");
            }

            ACTION_RUN => {
                if self.conn.cont().is_err() {
                    println!("Unable to run");
                }
            }

            ACTION_STEP => {
                let mut step_res = [0; 16];
                self.conn.step(&mut step_res).unwrap();
                    println!("step res {:?}", step_res);
                    self.get_registers(writer);
                }
            _ => (),
        }

    }

    fn register_menu(&mut self, menu_funcs: &mut MenuFuncs) -> *mut c_void {
        let menu = menu_funcs.create_menu("Amiga UAE Debugger");
        menu_funcs.add_menu_item(menu, "Connect to UAE...", 0, 0, 0);
        menu
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_backend_plugin!(PLUGIN, b"Amiga UAE Debugger\0", AmigaUaeBackend);
    plugin_handler.register_backend(&PLUGIN);
}

<commit_msg>WIP on getting dma streaming working<commit_after>#[macro_use]
extern crate prodbg_api;
extern crate gdb_remote;
use prodbg_api::*;
use std::os::raw::{c_void};
use gdb_remote::GdbRemote;

const MENU_CONNECT: u32 = 0;
const MENU_ENABLE_DMA: u32 = 1;

struct AmigaUaeBackend {
    capstone: Capstone,
    conn: GdbRemote,
    exception_location: u32,
    _id_amiga_uae_dma_time: u16,
}

impl AmigaUaeBackend {
    fn get_u32(data: &[u8]) -> u32 {
        let t0 = data[0] as u32;
        let t1 = data[1] as u32;
        let t2 = data[2] as u32;
        let t3 = data[3] as u32;
        (t0 << 24) | (t1 << 16) | (t2 << 8) | t3
    }

    fn write_register(writer: &mut Writer, name: &str, data: u32, read_only: bool) {
        writer.array_entry_begin();
        writer.write_string("name", name);
        writer.write_u8("size", 4);

        if read_only {
            writer.write_u8("read_only", 1);
        }

        writer.write_u32("register", data);

        writer.array_entry_end();
    }

    fn write_registers(&mut self, writer: &mut Writer, data: &[u8]) {
        let mut index = 0;

        writer.event_begin(EventType::SetRegisters as u16);
        writer.array_begin("registers");

        // d registers

        for i in 0..8 {
            let name = format!("d{}", i);
            let reg = Self::get_u32(&data[index..]);
            Self::write_register(writer, &name, reg, false);
            index += 4;
        }

        // a registers

        for i in 0..8 {
            let name = format!("a{}", i);
            let reg = Self::get_u32(&data[index..]);
            Self::write_register(writer, &name, reg, false);
            index += 4;
        }

        // Status registers & pc

        let sr = Self::get_u32(&data[index..]);
        let pc = Self::get_u32(&data[index + 4..]);

        self.exception_location = pc;

        Self::write_register(writer, "sr", sr, true);
        Self::write_register(writer, "pc", pc, true);

        writer.array_end();
        writer.event_end();
    }

    fn write_disassembly(&mut self, reader: &mut Reader, writer: &mut Writer) {
        match self.capstone.open(Arch::M68K, CS_MODE_M68K_000) {
            Err(e) => {
                println!("Unable to open Capstone {}", e as i32);
                return;
            }
            _ => (),
        }

        let mut scratch_string = String::with_capacity(256);

        let address = reader.find_u64("address_start").ok().unwrap();
        let count = reader.find_u32("instruction_count").ok().unwrap();

        let mut data = Vec::<u8>::with_capacity(256 * 1024);
        let memory_fetch_size = count * 4;

        if self.conn.get_memory(&mut data, address, memory_fetch_size as u64).is_err() {
            println!("Unable to fetch memory from {:x} - size {}", address, memory_fetch_size);
            return;
        }

        println!("disasm data address {:x} - len {}", address, data.len());

        if let Ok(insns) = self.capstone.disasm(&data, address as u64, 0) {
            writer.event_begin(EventType::SetDisassembly as u16);
            writer.array_begin("disassembly");
            let mut c = 0;

            for i in insns.iter() {
                let text = format!("{0: <10} {1: <10}", i.mnemonic().unwrap(), i.op_str().unwrap_or(""));
                writer.array_entry_begin();
                writer.write_u32("address", i.address as u32);
                writer.write_string("line", &text);

                scratch_string.clear();

                for register in i.regs_read().unwrap() {
                    let reg_name = self.capstone.reg_name(*register);
                    scratch_string.push_str(reg_name);
                    scratch_string.push(' ');
                }

                if scratch_string.len() > 0 {
                    let t = scratch_string.trim_right();
                    writer.write_string("registers_read", t);
                }

                scratch_string.clear();

                for register in i.regs_write().unwrap() {
                    let reg_name = self.capstone.reg_name(*register);
                    scratch_string.push_str(reg_name);
                    scratch_string.push(' ');
                }

                if scratch_string.len() > 0 {
                    let t = scratch_string.trim_right();
                    writer.write_string("registers_write", t);
                }

                writer.array_entry_end();

                c += 1;
            }

            println!("reported {} instructions", c);

            writer.array_end();
            writer.event_end();
        } else {
            println!("No instructions :(");
        }
    }

    fn get_memory(&mut self, reader: &mut Reader, writer: &mut Writer) {
        let mut data = Vec::<u8>::with_capacity(256 * 1024);

        let address = reader.find_u64("address_start").ok().unwrap();
        let size = reader.find_u32("size").ok().unwrap();

        if self.conn.get_memory(&mut data, address, size as u64).is_err() {
            println!("Unable to fetch memory from {:x} - size {}", address, size);
            return;
        }

        writer.event_begin(EventType::SetMemory as u16);
        writer.write_u64("address", address);
        writer.write_data("data", &data);
        writer.event_end();
    }

    //
    fn write_exception_location(&mut self, writer: &mut Writer) {
        writer.event_begin(EventType::SetExceptionLocation as u16);
        writer.write_u64("address", self.exception_location as u64);
        writer.write_u8("size", 4);
        writer.event_end();
    }

    fn set_breakpoint(&mut self, reader: &mut Reader, _writer: &mut Writer) {
       if let Some(address) = reader.find_u64("address").ok() {
           if self.conn.set_breakpoint_at_address(address).is_err() {
               println!("Unable to set breakpoint at 0x{:08x}", address);
           }
       }
    }

    fn delete_breakpoint(&mut self, reader: &mut Reader, _writer: &mut Writer) {
       if let Some(address) = reader.find_u64("address").ok() {
           if self.conn.remove_breakpoint_at_address(address).is_err() {
               println!("Unable to remove breakpoint at 0x{:08x}", address);
           }
       }
    }

    fn get_registers(&mut self, writer: &mut Writer) {
        let mut register_data = [0; 1024];
        if self.conn.get_registers(&mut register_data).is_ok() {
            println!("setting registers!");
            self.write_registers(writer, &register_data);
        }

        self.write_exception_location(writer);
    }

    fn update_conn_incoming(&mut self, writer: &mut Writer) {
        let mut should_break = false;

        if self.conn.is_connected() {
            if let Some(ref event) = self.conn.read_incoming_event() {
                println!("Got incoming {:?}", event.data);
                // Check if we got an exception back
                if let Some(ref _data) = event.begins_with("S") {
                    println!("Should break");
                    should_break = true;
                }
            }
        }

        if should_break {
            self.get_registers(writer);
        }
    }

    fn on_menu(&mut self, reader: &mut Reader) {
        let menu_id = reader.find_u32("menu_id").ok().unwrap();

        println!("menu id {}", menu_id);

        match menu_id {
            MENU_CONNECT => {
                if self.conn.connect("127.0.0.1:6860").is_ok() {
                    if self.conn.request_no_ack_mode().is_ok() {
                        println!("Connected. Ready to go!");
                        if self.conn.cont().is_err() {
                            println!("Failed to cont");
                        }
                    } else {
                        println!("no ack request failed");
                    }
                } else {
                    println!("Unable to connect to UAE");
                }
            }

            MENU_ENABLE_DMA => {
                if self.conn.is_connected() {
                    let mut res = [0; 256];
                    self.conn.send_command_wait_reply_raw(&mut res, "QDmaTimeEnable").unwrap();
                }
            }

            _ => (),
        }
    }
}

impl Backend for AmigaUaeBackend {
    fn new(service: &Service) -> Self {
        AmigaUaeBackend {
            capstone: service.get_capstone(),
            _id_amiga_uae_dma_time: service.get_id_register().register_id("AmigaUAEDmaTime"),
            conn: GdbRemote::new(),
            exception_location: 0,
        }
    }

    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer) {
        self.update_conn_incoming(writer);

        for event in reader.get_event() {
            match event {
                EVENT_MENU_EVENT => {
                    self.on_menu(reader);
                }
                EVENT_GET_DISASSEMBLY => {
                    self.write_disassembly(reader, writer);
                }

                EVENT_GET_MEMORY => {
                    self.get_memory(reader, writer);
                }

                EVENT_SET_BREAKPOINT => {
                    self.set_breakpoint(reader, writer);
                }

                EVENT_DELETE_BREAKPOINT => {
                    self.delete_breakpoint(reader, writer);
                }

                EVENT_GET_EXCEPTION_LOCATION => {
                    self.write_exception_location(writer);
                }

                _ => (),
            }
        }

        match action {
            ACTION_BREAK => {
                println!("Break");
            }

            ACTION_RUN => {
                if self.conn.cont().is_err() {
                    println!("Unable to run");
                }
            }

            ACTION_STEP => {
                let mut step_res = [0; 16];
                self.conn.step(&mut step_res).unwrap();
                    println!("step res {:?}", step_res);
                    self.get_registers(writer);
                }
            _ => (),
        }

    }

    fn register_menu(&mut self, menu_funcs: &mut MenuFuncs) -> *mut c_void {
        let menu = menu_funcs.create_menu("Amiga UAE Debugger");
        menu_funcs.add_menu_item(menu, "Connect to UAE...", MENU_CONNECT as usize, 0, 0);
        menu_funcs.add_menu_item(menu, "Enable DMA Stream", MENU_ENABLE_DMA as usize, 0, 0);
        menu
    }
}

#[no_mangle]
pub fn init_plugin(plugin_handler: &mut PluginHandler) {
    define_backend_plugin!(PLUGIN, b"Amiga UAE Debugger\0", AmigaUaeBackend);
    plugin_handler.register_backend(&PLUGIN);
}

<|endoftext|>


<commit_before>use syntax::{
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
    },
    AstNode, TextRange, T,
};

use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
    let assist_label = "Unwrap block";

    let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
    let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
    let target = block.syntax().text_range();
    let mut parent = block.syntax().parent()?;
    if ast::MatchArm::can_cast(parent.kind()) {
        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
    }

    let parent = ast::Expr::cast(parent)?;

    match parent.clone() {
        ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
        ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
        ast::Expr::IfExpr(if_expr) => {
            let then_branch = if_expr.then_branch()?;
            if then_branch == block {
                if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
                    // For `else if` blocks
                    let ancestor_then_branch = ancestor.then_branch()?;

                    return acc.add(assist_id, assist_label, target, |edit| {
                        let range_to_del_else_if = TextRange::new(
                            ancestor_then_branch.syntax().text_range().end(),
                            l_curly_token.text_range().start(),
                        );
                        let range_to_del_rest = TextRange::new(
                            then_branch.syntax().text_range().end(),
                            if_expr.syntax().text_range().end(),
                        );

                        edit.delete(range_to_del_rest);
                        edit.delete(range_to_del_else_if);
                        edit.replace(
                            target,
                            update_expr_string(then_branch.to_string(), &[' ', '{']),
                        );
                    });
                }
            } else {
                return acc.add(assist_id, assist_label, target, |edit| {
                    let range_to_del = TextRange::new(
                        then_branch.syntax().text_range().end(),
                        l_curly_token.text_range().start(),
                    );

                    edit.delete(range_to_del);
                    edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
                });
            }
        }
        _ => return None,
    };

    let unwrapped = unwrap_trivial_block(block);
    acc.add(assist_id, assist_label, target, |builder| {
        builder.replace(
            parent.syntax().text_range(),
            update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
        );
    })
}

fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
    let expr_string = expr_str.trim_start_matches(trim_start_pat);
    let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
    expr_string_lines.pop(); // Delete last line

    expr_string_lines
        .into_iter()
        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
        .collect::<Vec<String>>()
        .join("\n")
}

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

    use super::*;

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                } else {<|>
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                }
                println!("else");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_middle() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                } else {
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn unwrap_match_arm() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    match rel_path {
        Ok(rel_path) => {<|>
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    }
}
"#,
            r#"
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    Some((*id, rel_path))
}
"#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>**Unwrap Block** supports stand-alone blocks<commit_after>use syntax::{
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
    },
    AstNode, SyntaxKind, TextRange, T,
};

use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
    let assist_label = "Unwrap block";

    let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
    let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
    let target = block.syntax().text_range();
    let mut parent = block.syntax().parent()?;
    if ast::MatchArm::can_cast(parent.kind()) {
        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
    }

    if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) {
        return acc.add(assist_id, assist_label, target, |builder| {
            builder.replace(
                block.syntax().text_range(),
                update_expr_string(block.to_string(), &[' ', '{', '\n']),
            );
        });
    }

    let parent = ast::Expr::cast(parent)?;

    match parent.clone() {
        ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
        ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
        ast::Expr::IfExpr(if_expr) => {
            let then_branch = if_expr.then_branch()?;
            if then_branch == block {
                if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
                    // For `else if` blocks
                    let ancestor_then_branch = ancestor.then_branch()?;

                    return acc.add(assist_id, assist_label, target, |edit| {
                        let range_to_del_else_if = TextRange::new(
                            ancestor_then_branch.syntax().text_range().end(),
                            l_curly_token.text_range().start(),
                        );
                        let range_to_del_rest = TextRange::new(
                            then_branch.syntax().text_range().end(),
                            if_expr.syntax().text_range().end(),
                        );

                        edit.delete(range_to_del_rest);
                        edit.delete(range_to_del_else_if);
                        edit.replace(
                            target,
                            update_expr_string(then_branch.to_string(), &[' ', '{']),
                        );
                    });
                }
            } else {
                return acc.add(assist_id, assist_label, target, |edit| {
                    let range_to_del = TextRange::new(
                        then_branch.syntax().text_range().end(),
                        l_curly_token.text_range().start(),
                    );

                    edit.delete(range_to_del);
                    edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
                });
            }
        }
        _ => return None,
    };

    let unwrapped = unwrap_trivial_block(block);
    acc.add(assist_id, assist_label, target, |builder| {
        builder.replace(
            parent.syntax().text_range(),
            update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
        );
    })
}

fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
    let expr_string = expr_str.trim_start_matches(trim_start_pat);
    let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
    expr_string_lines.pop(); // Delete last line

    expr_string_lines
        .into_iter()
        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
        .collect::<Vec<String>>()
        .join("\n")
}

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

    use super::*;

    #[test]
    fn unwrap_tail_expr_block() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    <|>{
        92
    }
}
"#,
            r#"
fn main() {
    92
}
"#,
        )
    }

    #[test]
    fn unwrap_stmt_expr_block() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    <|>{
        92;
    }
    ()
}
"#,
            r#"
fn main() {
    92;
    ()
}
"#,
        );
        // Pedantically, we should add an `;` here...
        check_assist(
            unwrap_block,
            r#"
fn main() {
    <|>{
        92
    }
    ()
}
"#,
            r#"
fn main() {
    92
    ()
}
"#,
        );
    }

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                } else {<|>
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                }
                println!("else");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_middle() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                } else {
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn unwrap_match_arm() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    match rel_path {
        Ok(rel_path) => {<|>
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    }
}
"#,
            r#"
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    Some((*id, rel_path))
}
"#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::rc::Rc;

use output::Output;

use sha2::Digest;
use sha2::Sha256;

use time;

use arguments::*;
use misc::*;
use storage::*;
use types::*;

pub struct ContentHasher {
	pub num_ignored: u64,
	pub num_fresh: u64,
	pub num_updated: u64,
	pub num_remaining: u64,
}

impl ContentHasher {

	pub fn new (
	) -> ContentHasher {

		ContentHasher {
			num_ignored: 0,
			num_fresh: 0,
			num_updated: 0,
			num_remaining: 0,
		}

	}

	pub fn calculate_hashes (
		& mut self,
		arguments: & Arguments,
		output: & mut Output,
		previous_database: FileDatabase,
	) -> Result <FileDatabase, String> {

		let mut new_database: FileDatabase =
			FileDatabase::new ();

		let mut num_ignored = 0;
		let mut num_fresh = 0;
		let mut num_remaining = 0;
		let mut num_updated = 0;

		let mut size_hashed: u64 = 0;

		let root_set: HashSet <Rc <PathBuf>> =
			arguments.root_paths.iter ().map (
				|root_path|
				root_path.clone ()
			).collect ();

		for file_data in previous_database.into_iter () {

			if (

				(

					file_data.root_path.is_none ()

				) || (

					file_data.root_path.is_some ()

					&& ! root_set.contains (
						& file_data.root_path.as_ref ().unwrap ().clone ())

				)

			) {

				new_database.insert_direct (
					file_data);

				num_ignored += 1;

			} else if file_data.content_hash != ZERO_HASH {

				new_database.insert_direct (
					file_data);

				num_fresh += 1;

			} else if (
				num_updated > 0
				&& size_hashed + file_data.size > arguments.content_hash_batch_size
			) {

				new_database.insert_direct (
					file_data);

				num_remaining += 1;

			} else {

				output.status (
					& format! (
						"Content hash: {}",
						file_data.path.to_string_lossy ()));

				let content_hash_time =
					time::get_time ();

				let content_hash =
					try! (
						calculate_hash_for_file (
							file_data.path.clone ()));

				new_database.insert_update_content_hash (
					& file_data,
					file_data.root_path.as_ref ().unwrap ().clone (),
					content_hash,
					content_hash_time.sec);

				size_hashed += file_data.size;
				num_updated += 1;

			}

		}

		self.num_ignored = num_ignored;
		self.num_fresh = num_fresh;
		self.num_remaining = num_remaining;
		self.num_updated += num_updated;

		output.clear_status ();

		Ok (new_database)

	}

}

fn calculate_hash_for_file (
	path: PathRef,
) -> Result <Hash, String> {

	let mut file =
		try! (
			io_result (
				File::open (
					path.as_ref ())));

	let mut hasher =
		Sha256::new ();

	let mut buffer: [u8; 0x1000] =
		[0u8; 0x1000];

	loop {

		let bytes_read =
			try! (
				io_result (
					file.read (
						& mut buffer)));

		if bytes_read == 0 {
			break;
		}

		hasher.input (
			& buffer [
				0 .. bytes_read]);

	}

	let mut result: Hash =
		[0u8; HASH_SIZE];

	result.copy_from_slice (
		& hasher.result ());

	Ok (result)

}

pub fn calculate_content_hashes (
	arguments: & Arguments,
	output: & mut Output,
	file_database: FileDatabase,
) -> Result <FileDatabase, String> {

	let mut file_database =
		file_database;

	let mut content_hasher =
		ContentHasher::new ();

	loop {

		// calculate a batch of hashes

		file_database =
			try! (
				content_hasher.calculate_hashes (
					arguments,
					output,
					file_database));

		if content_hasher.num_remaining == 0 {
			break;
		}

		output.message (
			& format! (
				"Hashed contents of {} out of {} files, {} remaining",
				content_hasher.num_updated,
				content_hasher.num_updated + content_hasher.num_remaining,
				content_hasher.num_remaining));

		// write out updated database

		try! (
			write_database (
				& arguments,
				output,
				& file_database));

	}

	output.message (
		& format! (
			"Hashed contents of {} files, ignored {} with fresh hashes",
			content_hasher.num_updated,
			content_hasher.num_fresh));

	// write out updated database

	if content_hasher.num_updated > 0 {

		try! (
			write_database (
				& arguments,
				output,
				& file_database));

	}

	Ok (file_database)

}

// ex: noet ts=4 filetype=rust
<commit_msg>ignore errors during content hash, show count at end<commit_after>use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::rc::Rc;

use output::Output;

use sha2::Digest;
use sha2::Sha256;

use time;

use arguments::*;
use misc::*;
use storage::*;
use types::*;

pub struct ContentHasher {
	num_ignored: u64,
	num_fresh: u64,
	num_updated: u64,
	num_remaining: u64,
	num_errors: u64,
}

impl ContentHasher {

	pub fn new (
	) -> ContentHasher {

		ContentHasher {
			num_ignored: 0,
			num_fresh: 0,
			num_updated: 0,
			num_remaining: 0,
			num_errors: 0,
		}

	}

	pub fn calculate_hashes (
		& mut self,
		arguments: & Arguments,
		output: & mut Output,
		previous_database: FileDatabase,
	) -> FileDatabase {

		let mut new_database: FileDatabase =
			FileDatabase::new ();

		let mut num_ignored: u64 = 0;
		let mut num_fresh: u64 = 0;
		let mut num_remaining: u64 = 0;
		let mut num_updated: u64 = 0;
		let mut num_errors: u64 = 0;

		let mut size_hashed: u64 = 0;

		let root_set: HashSet <Rc <PathBuf>> =
			arguments.root_paths.iter ().map (
				|root_path|
				root_path.clone ()
			).collect ();

		for file_data in previous_database.into_iter () {

			if (

				(

					file_data.root_path.is_none ()

				) || (

					file_data.root_path.is_some ()

					&& ! root_set.contains (
						& file_data.root_path.as_ref ().unwrap ().clone ())

				)

			) {

				new_database.insert_direct (
					file_data);

				num_ignored += 1;

			} else if file_data.content_hash != ZERO_HASH {

				new_database.insert_direct (
					file_data);

				num_fresh += 1;

			} else if (
				num_updated > 0
				&& size_hashed + file_data.size > arguments.content_hash_batch_size
			) {

				new_database.insert_direct (
					file_data);

				num_remaining += 1;

			} else {

				output.status (
					& format! (
						"Content hash: {}",
						file_data.path.to_string_lossy ()));

				let content_hash_time =
					time::get_time ();

				if let Ok (content_hash) = (
					calculate_hash_for_file (
						file_data.path.clone ())
				) {

					new_database.insert_update_content_hash (
						& file_data,
						file_data.root_path.as_ref ().unwrap ().clone (),
						content_hash,
						content_hash_time.sec);

					size_hashed += file_data.size;
					num_updated += 1;

				} else {

					num_errors += 1;

				}

			}

		}

		self.num_ignored = num_ignored;
		self.num_fresh = num_fresh;
		self.num_remaining = num_remaining;
		self.num_updated += num_updated;
		self.num_errors += num_errors;

		output.clear_status ();

		new_database

	}

	pub fn num_ignored (& self) -> u64 {
		self.num_ignored
	}

	pub fn num_fresh (& self) -> u64 {
		self.num_fresh
	}

	pub fn num_updated (& self) -> u64 {
		self.num_updated
	}

	pub fn num_remaining (& self) -> u64 {
		self.num_remaining
	}

	pub fn num_errors (& self) -> u64 {
		self.num_errors
	}

	pub fn num_processed (& self) -> u64 {
		self.num_updated + self.num_errors
	}

	pub fn num_to_process (& self) -> u64 {
		self.num_updated + self.num_errors + self.num_remaining
	}

}

fn calculate_hash_for_file (
	path: PathRef,
) -> Result <Hash, String> {

	let mut file =
		try! (
			io_result (
				File::open (
					path.as_ref ())));

	let mut hasher =
		Sha256::new ();

	let mut buffer: [u8; 0x1000] =
		[0u8; 0x1000];

	loop {

		let bytes_read =
			try! (
				io_result (
					file.read (
						& mut buffer)));

		if bytes_read == 0 {
			break;
		}

		hasher.input (
			& buffer [
				0 .. bytes_read]);

	}

	let mut result: Hash =
		[0u8; HASH_SIZE];

	result.copy_from_slice (
		& hasher.result ());

	Ok (result)

}

pub fn calculate_content_hashes (
	arguments: & Arguments,
	output: & mut Output,
	file_database: FileDatabase,
) -> Result <FileDatabase, String> {

	let mut file_database =
		file_database;

	let mut content_hasher =
		ContentHasher::new ();

	loop {

		// calculate a batch of hashes

		file_database =
			content_hasher.calculate_hashes (
				arguments,
				output,
				file_database);

		if content_hasher.num_remaining () == 0 {
			break;
		}

		output.message (
			& format! (
				"Hashed contents of {} out of {} files, {} remaining",
				content_hasher.num_processed (),
				content_hasher.num_to_process (),
				content_hasher.num_remaining));

		// write out updated database

		try! (
			write_database (
				& arguments,
				output,
				& file_database));

	}

	output.message (
		& format! (
			"Hashed contents of {} files with {} errors, ignored {} with fresh \
			hashes",
			content_hasher.num_updated (),
			content_hasher.num_errors (),
			content_hasher.num_fresh ()));

	// write out updated database

	if content_hasher.num_updated > 0 {

		try! (
			write_database (
				& arguments,
				output,
				& file_database));

	}

	Ok (file_database)

}

// ex: noet ts=4 filetype=rust
<|endoftext|>


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

extern crate serde_json;
extern crate collections;

use self::collections::vec::IntoIter;
use service::Service;
use std::collections::HashMap;
use std::io::Error;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::{ Arc, Mutex };

// The `global` context available to all.
pub struct Context {
    pub verbose: bool,

    hostname: String,
    http_port: u16,
    ws_port: u16,
    services: HashMap<String, Box<Service>>
}

const DEFAULT_HTTP_PORT: u16 = 3000;
const DEFAULT_WS_PORT: u16 = 4000;
const DEFAULT_HOSTNAME: &'static str = "::"; // ipv6 default.

pub type SharedContext = Arc<Mutex<Context>>;

impl Context {
    pub fn new(verbose: bool, hostname: Option<String>) -> Context {
        Context { services: HashMap::new(),
                  verbose: verbose,
                  hostname:  hostname.unwrap_or(DEFAULT_HOSTNAME.to_owned()),
                  http_port: DEFAULT_HTTP_PORT,
                  ws_port: DEFAULT_WS_PORT }
    }

    pub fn shared(verbose: bool, hostname: Option<String>) -> SharedContext {
        Arc::new(Mutex::new(Context::new(verbose, hostname)))
    }

    pub fn add_service(&mut self, service: Box<Service>) {
        let service_id = service.get_properties().id;
        self.services.insert(service_id, service);
    }

    pub fn remove_service(&mut self, id: String) {
        self.services.remove(&id);
    }

    pub fn services_count(&self) -> usize {
        self.services.len()
    }

    pub fn get_service(&self, id: &str) -> Option<&Box<Service>> {
        self.services.get(id)
    }

    pub fn services_as_json(&self) -> Result<String, serde_json::error::Error> {
        serde_json::to_string(&self.services)
    }

    pub fn get_http_root_for_service(&self, service_id: String) -> String {
        format!("http://{}:{}/services/{}/", self.hostname, self.http_port, service_id)
    }

    pub fn get_ws_root_for_service(&self, service_id: String) -> String {
        format!("ws://{}:{}/services/{}/", self.hostname, self.ws_port, service_id)
    }

    pub fn http_as_addrs(&self) -> Result<IntoIter<SocketAddr>, Error> {
        (self.hostname.as_str(), self.http_port).to_socket_addrs()
    }
}

#[test]
#[allow(unused_variables)]
fn test_should_add_a_service() {
    use service::{ Service, ServiceProperties };
    use iron::{Request, Response, IronResult};

    struct ServiceStub;

    impl Service for ServiceStub {
        fn get_properties(&self) -> ServiceProperties {
            ServiceProperties {
                id: '1'.to_string(),
                name: "dummy service".to_string(),
                description: "really nothing to see".to_string(),
                http_url: '2'.to_string(),
                ws_url: '3'.to_string()
            }
        }
        fn start(&self)  {}
        fn stop(&self) {}
        fn process_request(&self, req: &Request) -> IronResult<Response> {
            return Ok(Response::new());
        }
    }

    let service = ServiceStub;
    let mut foo = Context::new(false, Some("localhost".to_owned()));

    assert_eq!(foo.services.is_empty(), true);
    foo.add_service(Box::new(service));
    assert_eq!(foo.services.is_empty(), false);
}
<commit_msg>Lint unit test<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;
extern crate collections;

use self::collections::vec::IntoIter;
use service::Service;
use std::collections::HashMap;
use std::io::Error;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::{ Arc, Mutex };

// The `global` context available to all.
pub struct Context {
    pub verbose: bool,

    hostname: String,
    http_port: u16,
    ws_port: u16,
    services: HashMap<String, Box<Service>>
}

const DEFAULT_HTTP_PORT: u16 = 3000;
const DEFAULT_WS_PORT: u16 = 4000;
const DEFAULT_HOSTNAME: &'static str = "::"; // ipv6 default.

pub type SharedContext = Arc<Mutex<Context>>;

impl Context {
    pub fn new(verbose: bool, hostname: Option<String>) -> Context {
        Context { services: HashMap::new(),
                  verbose: verbose,
                  hostname:  hostname.unwrap_or(DEFAULT_HOSTNAME.to_owned()),
                  http_port: DEFAULT_HTTP_PORT,
                  ws_port: DEFAULT_WS_PORT }
    }

    pub fn shared(verbose: bool, hostname: Option<String>) -> SharedContext {
        Arc::new(Mutex::new(Context::new(verbose, hostname)))
    }

    pub fn add_service(&mut self, service: Box<Service>) {
        let service_id = service.get_properties().id;
        self.services.insert(service_id, service);
    }

    pub fn remove_service(&mut self, id: String) {
        self.services.remove(&id);
    }

    pub fn services_count(&self) -> usize {
        self.services.len()
    }

    pub fn get_service(&self, id: &str) -> Option<&Box<Service>> {
        self.services.get(id)
    }

    pub fn services_as_json(&self) -> Result<String, serde_json::error::Error> {
        serde_json::to_string(&self.services)
    }

    pub fn get_http_root_for_service(&self, service_id: String) -> String {
        format!("http://{}:{}/services/{}/", self.hostname, self.http_port, service_id)
    }

    pub fn get_ws_root_for_service(&self, service_id: String) -> String {
        format!("ws://{}:{}/services/{}/", self.hostname, self.ws_port, service_id)
    }

    pub fn http_as_addrs(&self) -> Result<IntoIter<SocketAddr>, Error> {
        (self.hostname.as_str(), self.http_port).to_socket_addrs()
    }
}

#[test]
#[allow(unused_variables)]
fn test_should_add_a_service() {
    use service::{ Service, ServiceProperties };
    use iron::{Request, Response, IronResult};

    struct ServiceStub;

    impl Service for ServiceStub {
        fn get_properties(&self) -> ServiceProperties {
            ServiceProperties {
                id: '1'.to_string(),
                name: "dummy service".to_owned(),
                description: "really nothing to see".to_owned(),
                http_url: '2'.to_string(),
                ws_url: '3'.to_string()
            }
        }
        fn start(&self)  {}
        fn stop(&self) {}
        fn process_request(&self, req: &Request) -> IronResult<Response> {
            Ok(Response::new())
        }
    }

    let service = ServiceStub;
    let mut foo = Context::new(false, Some("localhost".to_owned()));

    assert_eq!(foo.services.is_empty(), true);
    foo.add_service(Box::new(service));
    assert_eq!(foo.services.is_empty(), false);
}
<|endoftext|>