<commit_before>extern crate nannou;
extern crate shade_runner;

use nannou::prelude::*;
use std::cell::RefCell;
use std::ffi::CStr;
use std::sync::Arc;
use std::time::Duration;

struct Model {
    render_pass: Arc<vk::RenderPassAbstract + Send + Sync>,
    pipeline: Option<Arc<vk::GraphicsPipelineAbstract + Send + Sync>>,
    vertex_buffer: Arc<vk::CpuAccessibleBuffer<[Vertex]>>,
    view_fbo: RefCell<ViewFbo>,
    shade_watcher: shade_runner::Watch,
    shade_msg: shade_runner::Message,
    vert_shader: Arc<vk::pipeline::shader::ShaderModule>,
    frag_shader: Arc<vk::pipeline::shader::ShaderModule>,
    device: Arc<vk::Device>,
}

#[derive(Debug, Clone)]
struct Vertex {
    position: [f32; 2],
}

vk::impl_vertex!(Vertex, position);

fn main() {
    nannou::app(model).view(view).update(update).run();
}

fn model(app: &App) -> Model {
    app.new_window()
        .with_dimensions(512, 512)
        .view(view)
        .build()
        .unwrap();

    // The gpu device associated with the window's swapchain
    let device = app.main_window().swapchain().device().clone();

    // We now create a buffer that will store the shape of our triangle.
    let vertex_buffer = {
        let positions = [[-0.5, -0.25], [0.0, 0.5], [0.25, -0.1]];
        let vertices = positions.iter().map(|&position| Vertex { position });
        vk::CpuAccessibleBuffer::from_iter(device.clone(), vk::BufferUsage::all(), vertices)
            .unwrap()
    };
    let vert_path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vulkan/shaders/hotload_vert.glsl");
    let frag_path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vulkan/shaders/hotload_frag.glsl");
    let shade_watcher = shade_runner::Watch::create(vert_path, frag_path, Duration::from_millis(50)).expect("failed to create watcher");
    let shade_msg = shade_watcher
        .rx
        .recv()
        .expect("Failed to receive shader")
        .expect("failed to compile shader");
    let vs = unsafe {
        vk::pipeline::shader::ShaderModule::from_words(device.clone(), &shade_msg.shaders.vertex)
    }
    .unwrap();

    let fs = unsafe {
        vk::pipeline::shader::ShaderModule::from_words(device.clone(), &shade_msg.shaders.fragment)
    }
    .unwrap();

    // The next step is to create a *render pass*, which is an object that describes where the
    // output of the graphics pipeline will go. It describes the layout of the images
    // where the colors, depth and/or stencil information will be written.
    let render_pass = Arc::new(
        vk::single_pass_renderpass!(
            device.clone(),
            attachments: {
                // `color` is a custom name we give to the first and only attachment.
                color: {
                    // `load: Clear` means that we ask the GPU to clear the content of this
                    // attachment at the start of the drawing.
                    load: Clear,
                    // `store: Store` means that we ask the GPU to store the output of the draw
                    // in the actual image. We could also ask it to discard the result.
                    store: Store,
                    // `format: <ty>` indicates the type of the format of the image. This has to
                    // be one of the types of the `vulkano::format` module (or alternatively one
                    // of your structs that implements the `FormatDesc` trait). Here we use the
                    // same format as the swapchain.
                    format: app.main_window().swapchain().format(),
                    // TODO:
                    samples: app.main_window().msaa_samples(),
                }
            },
            pass: {
                // We use the attachment named `color` as the one and only color attachment.
                color: [color],
                // No depth-stencil attachment is indicated with empty brackets.
                depth_stencil: {}
            }
        )
        .unwrap(),
    );

    let pipeline = None;
    // The render pass we created above only describes the layout of our framebuffer. Before we
    // can draw we also need to create the actual framebuffer.
    let view_fbo = RefCell::new(ViewFbo::default());

    let mut model = Model {
        render_pass,
        pipeline,
        vertex_buffer,
        view_fbo,
        shade_watcher,
        shade_msg,
        vert_shader: vs,
        frag_shader: fs,
        device,
    };
    update_pipeline(&mut model);
    model
}

fn update(_app: &App, model: &mut Model, _: Update) {
    let shader_msg = model.shade_watcher.rx.try_iter().last();
    if let Some(shade_msg) = shader_msg {
        match shade_msg {
            Ok(shade_msg) => {
                model.vert_shader = unsafe {
                    vk::pipeline::shader::ShaderModule::from_words(
                        model.device.clone(),
                        &shade_msg.shaders.vertex,
                    )
                }
                .unwrap();

                model.frag_shader = unsafe {
                    vk::pipeline::shader::ShaderModule::from_words(
                        model.device.clone(),
                        &shade_msg.shaders.fragment,
                    )
                }
                .unwrap();
                model.shade_msg = shade_msg;
                update_pipeline(model);
            }
            Err(e) => {
                println!("Error compiling shader {:?}", e);
            }
        }
    }
}

// Draw the state of your `Model` into the given `Frame` here.
fn view(_app: &App, model: &Model, frame: Frame) -> Frame {
    // Dynamic viewports allow us to recreate just the viewport when the window is resized
    // Otherwise we would have to recreate the whole pipeline.
    let [w, h] = frame.swapchain_image().dimensions();
    let viewport = vk::ViewportBuilder::new().build([w as _, h as _]);
    let dynamic_state = vk::DynamicState::default().viewports(vec![viewport]);

    // Update the view_fbo.
    model
        .view_fbo
        .borrow_mut()
        .update(&frame, model.render_pass.clone(), |builder, image| {
            builder.add(image)
        })
        .unwrap();

    // Specify the color to clear the framebuffer with i.e. blue.
    let clear_values = vec![[0.0, 0.0, 1.0, 1.0].into()];

    // Submit the draw commands.
    frame
        .add_commands()
        .begin_render_pass(model.view_fbo.borrow().expect_inner(), false, clear_values)
        .unwrap()
        .draw(
            model.pipeline.clone().unwrap(),
            &dynamic_state,
            vec![model.vertex_buffer.clone()],
            (),
            (),
        )
        .unwrap()
        .end_render_pass()
        .expect("failed to add `end_render_pass` command");

    frame
}

fn update_pipeline(model: &mut Model) {
    let Model {
        ref vert_shader,
        ref frag_shader,
        ref shade_msg,
        ref device,
        ref render_pass,
        ref mut pipeline,
        ..
    } = model;
    let entry = shade_msg.entry.clone();
    let vert_main = unsafe {
        vert_shader.graphics_entry_point(
            CStr::from_bytes_with_nul_unchecked(b"main\0"),
            entry.vert_input,
            entry.vert_output,
            entry.vert_layout,
            vk::pipeline::shader::GraphicsShaderType::Vertex,
        )
    };
    let frag_main = unsafe {
        frag_shader.graphics_entry_point(
            CStr::from_bytes_with_nul_unchecked(b"main\0"),
            entry.frag_input,
            entry.frag_output,
            entry.frag_layout,
            vk::pipeline::shader::GraphicsShaderType::Fragment,
        )
    };
    *pipeline = Some(Arc::new(
        vk::GraphicsPipeline::start()
            // We need to indicate the layout of the vertices.
            // The type `SingleBufferDefinition` actually contains a template parameter
            // corresponding to the type of each vertex.
            .vertex_input_single_buffer::<Vertex>()
            // A Vulkan shader can in theory contain multiple entry points, so we have to specify
            // which one. The `main` word of `main_entry_point` actually corresponds to the name of
            // the entry point.
            .vertex_shader(vert_main, ())
            // The content of the vertex buffer describes a list of triangles.
            .triangle_list()
            // Use a resizable viewport set to draw over the entire window
            .viewports_dynamic_scissors_irrelevant(1)
            // See `vertex_shader`.
            .fragment_shader(frag_main, ())
            // We have to indicate which subpass of which render pass this pipeline is going to be
            // used in. The pipeline will only be usable from this particular subpass.
            .render_pass(vk::Subpass::from(render_pass.clone(), 0).unwrap())
            // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
            .build(device.clone())
            .unwrap(),
    ));
}
<commit_msg>add comments<commit_after>extern crate nannou;
extern crate shade_runner;

use nannou::prelude::*;
use std::cell::RefCell;
use std::ffi::CStr;
use std::sync::Arc;
use std::time::Duration;

struct Model {
    render_pass: Arc<vk::RenderPassAbstract + Send + Sync>,
    pipeline: Option<Arc<vk::GraphicsPipelineAbstract + Send + Sync>>,
    vertex_buffer: Arc<vk::CpuAccessibleBuffer<[Vertex]>>,
    view_fbo: RefCell<ViewFbo>,
    shade_watcher: shade_runner::Watch,
    shade_msg: shade_runner::Message,
    vert_shader: Arc<vk::pipeline::shader::ShaderModule>,
    frag_shader: Arc<vk::pipeline::shader::ShaderModule>,
    device: Arc<vk::Device>,
}

#[derive(Debug, Clone)]
struct Vertex {
    position: [f32; 2],
}

vk::impl_vertex!(Vertex, position);

fn main() {
    nannou::app(model).view(view).update(update).run();
}

fn model(app: &App) -> Model {
    app.new_window()
        .with_dimensions(512, 512)
        .view(view)
        .build()
        .unwrap();

    // The gpu device associated with the window's swapchain
    let device = app.main_window().swapchain().device().clone();

    // We now create a buffer that will store the shape of our triangle.
    let vertex_buffer = {
        let positions = [[-0.5, -0.25], [0.0, 0.5], [0.25, -0.1]];
        let vertices = positions.iter().map(|&position| Vertex { position });
        vk::CpuAccessibleBuffer::from_iter(device.clone(), vk::BufferUsage::all(), vertices)
            .unwrap()
    };

    // Get the paths to your vertex and fragment shaders.
    let vert_path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vulkan/shaders/hotload_vert.glsl");
    let frag_path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/vulkan/shaders/hotload_frag.glsl");
    // Create a watcher that will reload both shaders if there is any change to the
    // parent directory eg. '/examples/vulkan/shaders/'
    let shade_watcher = shade_runner::Watch::create(vert_path, frag_path, Duration::from_millis(50)).expect("failed to create watcher");
    // Wait on the first message,
    // which is the shaders compiling and parsing.
    // The message is a Result which indicates if
    // the shader successfully compiled and parsed.
    let shade_msg = shade_watcher
        .rx
        .recv()
        .expect("Failed to receive shader")
        .expect("failed to compile shader");
    // Create the shader module from the compiled
    // shader in the message. It is simply
    // a Vec<u8>.
    let vs = unsafe {
        vk::pipeline::shader::ShaderModule::from_words(device.clone(), &shade_msg.shaders.vertex)
    }
    .unwrap();

    let fs = unsafe {
        vk::pipeline::shader::ShaderModule::from_words(device.clone(), &shade_msg.shaders.fragment)
    }
    .unwrap();

    // The next step is to create a *render pass*, which is an object that describes where the
    // output of the graphics pipeline will go. It describes the layout of the images
    // where the colors, depth and/or stencil information will be written.
    let render_pass = Arc::new(
        vk::single_pass_renderpass!(
            device.clone(),
            attachments: {
                // `color` is a custom name we give to the first and only attachment.
                color: {
                    // `load: Clear` means that we ask the GPU to clear the content of this
                    // attachment at the start of the drawing.
                    load: Clear,
                    // `store: Store` means that we ask the GPU to store the output of the draw
                    // in the actual image. We could also ask it to discard the result.
                    store: Store,
                    // `format: <ty>` indicates the type of the format of the image. This has to
                    // be one of the types of the `vulkano::format` module (or alternatively one
                    // of your structs that implements the `FormatDesc` trait). Here we use the
                    // same format as the swapchain.
                    format: app.main_window().swapchain().format(),
                    // TODO:
                    samples: app.main_window().msaa_samples(),
                }
            },
            pass: {
                // We use the attachment named `color` as the one and only color attachment.
                color: [color],
                // No depth-stencil attachment is indicated with empty brackets.
                depth_stencil: {}
            }
        )
        .unwrap(),
    );

    let pipeline = None;
    // The render pass we created above only describes the layout of our framebuffer. Before we
    // can draw we also need to create the actual framebuffer.
    let view_fbo = RefCell::new(ViewFbo::default());

    let mut model = Model {
        render_pass,
        pipeline,
        vertex_buffer,
        view_fbo,
        shade_watcher,
        shade_msg,
        vert_shader: vs,
        frag_shader: fs,
        device,
    };
    // Here we won't to update the pipeline but
    // we need data from the model so it has to
    // happen after model is created.
    update_pipeline(&mut model);
    model
}

fn update(_app: &App, model: &mut Model, _: Update) {
    // Get the latest message from the watcher.
    // There will be a message for any change to the shaders.
    let shader_msg = model.shade_watcher.rx.try_iter().last();
    if let Some(shade_msg) = shader_msg {
        match shade_msg {
            Ok(shade_msg) => {
                // Got a successfully compiled and parsed message.
                // Recreate the shader modules and update the pipeline.
                model.vert_shader = unsafe {
                    vk::pipeline::shader::ShaderModule::from_words(
                        model.device.clone(),
                        &shade_msg.shaders.vertex,
                    )
                }
                .unwrap();

                model.frag_shader = unsafe {
                    vk::pipeline::shader::ShaderModule::from_words(
                        model.device.clone(),
                        &shade_msg.shaders.fragment,
                    )
                }
                .unwrap();
                model.shade_msg = shade_msg;
                update_pipeline(model);
            }
            Err(e) => {
                // The shader changed but failed to compile.
                // This contains the reason why it failed.
                println!("Error compiling shader {:?}", e);
            }
        }
    }
}

// Draw the state of your `Model` into the given `Frame` here.
fn view(_app: &App, model: &Model, frame: Frame) -> Frame {
    // Dynamic viewports allow us to recreate just the viewport when the window is resized
    // Otherwise we would have to recreate the whole pipeline.
    let [w, h] = frame.swapchain_image().dimensions();
    let viewport = vk::ViewportBuilder::new().build([w as _, h as _]);
    let dynamic_state = vk::DynamicState::default().viewports(vec![viewport]);

    // Update the view_fbo.
    model
        .view_fbo
        .borrow_mut()
        .update(&frame, model.render_pass.clone(), |builder, image| {
            builder.add(image)
        })
        .unwrap();

    // Specify the color to clear the framebuffer with i.e. blue.
    let clear_values = vec![[0.0, 0.0, 1.0, 1.0].into()];

    // Submit the draw commands.
    frame
        .add_commands()
        .begin_render_pass(model.view_fbo.borrow().expect_inner(), false, clear_values)
        .unwrap()
        .draw(
            model.pipeline.clone().unwrap(),
            &dynamic_state,
            vec![model.vertex_buffer.clone()],
            (),
            (),
        )
        .unwrap()
        .end_render_pass()
        .expect("failed to add `end_render_pass` command");

    frame
}

fn update_pipeline(model: &mut Model) {
    let Model {
        ref vert_shader,
        ref frag_shader,
        ref shade_msg,
        ref device,
        ref render_pass,
        ref mut pipeline,
        ..
    } = model;
    // Here we use the other part of the latest message
    // from shade_runner. It is the entry point for vulkano
    // to use your compiled shader.
    // If there is any errors here make sure you have the
    // same version of vulkano in your application and shade_runner.
    // Cargo patch can be handy for this.
    let entry = shade_msg.entry.clone();
    let vert_main = unsafe {
        vert_shader.graphics_entry_point(
            CStr::from_bytes_with_nul_unchecked(b"main\0"),
            entry.vert_input,
            entry.vert_output,
            entry.vert_layout,
            vk::pipeline::shader::GraphicsShaderType::Vertex,
        )
    };
    let frag_main = unsafe {
        frag_shader.graphics_entry_point(
            CStr::from_bytes_with_nul_unchecked(b"main\0"),
            entry.frag_input,
            entry.frag_output,
            entry.frag_layout,
            vk::pipeline::shader::GraphicsShaderType::Fragment,
        )
    };
    *pipeline = Some(Arc::new(
        vk::GraphicsPipeline::start()
            // We need to indicate the layout of the vertices.
            // The type `SingleBufferDefinition` actually contains a template parameter
            // corresponding to the type of each vertex.
            .vertex_input_single_buffer::<Vertex>()
            // A Vulkan shader can in theory contain multiple entry points, so we have to specify
            // which one. The `main` word of `main_entry_point` actually corresponds to the name of
            // the entry point.
            .vertex_shader(vert_main, ())
            // The content of the vertex buffer describes a list of triangles.
            .triangle_list()
            // Use a resizable viewport set to draw over the entire window
            .viewports_dynamic_scissors_irrelevant(1)
            // See `vertex_shader`.
            .fragment_shader(frag_main, ())
            // We have to indicate which subpass of which render pass this pipeline is going to be
            // used in. The pipeline will only be usable from this particular subpass.
            .render_pass(vk::Subpass::from(render_pass.clone(), 0).unwrap())
            // Now that our builder is filled, we call `build()` to obtain an actual pipeline.
            .build(device.clone())
            .unwrap(),
    ));
}
<|endoftext|>


<commit_before>use pdb::{FallibleIterator, Rva, PDB};

#[test]
fn test_module_lines() {
    let file = std::fs::File::open("fixtures/self/foo.pdb").expect("opening file");
    let mut pdb = PDB::open(file).expect("parse pdb");

    let address_map = pdb.address_map().expect("address map");
    let string_table = pdb.string_table().expect("string table");

    let dbi = pdb.debug_information().expect("dbi");
    let mut modules = dbi.modules().expect("modules");
    let module = modules.next().expect("parse module").expect("no module");
    let module_info = pdb
        .module_info(&module)
        .expect("parse module info")
        .expect("module info");

    let line_program = module_info.line_program().expect("line program");
    let mut lines = line_program.lines();
    let line_info = lines.next().expect("parse line info").expect("no lines");

    let rva = line_info.offset.to_rva(&address_map).expect("line rva");
    let file_info = line_program
        .get_file_info(line_info.file_index)
        .expect("file info");
    let file_name = file_info
        .name
        .to_string_lossy(&string_table)
        .expect("file name");

    assert_eq!(line_info.line_start, 29);
    assert_eq!(line_info.column_start, Some(0)); // looks like useless column info
    assert_eq!(rva, Rva(0x64f0));
    assert_eq!(file_name, "c:\\users\\user\\desktop\\self\\foo.cpp");
}
<commit_msg>Fix test_module_lines without column information<commit_after>use pdb::{FallibleIterator, Rva, PDB};

#[test]
fn test_module_lines() {
    let file = std::fs::File::open("fixtures/self/foo.pdb").expect("opening file");
    let mut pdb = PDB::open(file).expect("parse pdb");

    let address_map = pdb.address_map().expect("address map");
    let string_table = pdb.string_table().expect("string table");

    let dbi = pdb.debug_information().expect("dbi");
    let mut modules = dbi.modules().expect("modules");
    let module = modules.next().expect("parse module").expect("no module");
    let module_info = pdb
        .module_info(&module)
        .expect("parse module info")
        .expect("module info");

    let line_program = module_info.line_program().expect("line program");
    let mut lines = line_program.lines();
    let line_info = lines.next().expect("parse line info").expect("no lines");

    let rva = line_info.offset.to_rva(&address_map).expect("line rva");
    let file_info = line_program
        .get_file_info(line_info.file_index)
        .expect("file info");
    let file_name = file_info
        .name
        .to_string_lossy(&string_table)
        .expect("file name");

    assert_eq!(line_info.line_start, 29);
    assert_eq!(line_info.column_start, None);
    assert_eq!(rva, Rva(0x64f0));
    assert_eq!(file_name, "c:\\users\\user\\desktop\\self\\foo.cpp");
}
<|endoftext|>


<commit_before>#[cfg(feature = "chacha20")] pub mod chacha20;

trait StreamEncrypt {
    fn encrypt_stream<I, O>(&mut self, input: I, output: O)
        where I: AsRef<[u8]>,
              O: AsMut<[u8]>;
}

trait StreamDecrypt {
    fn decrypt_stream<I, O>(&mut self, input: I, output: O)
        where I: AsRef<[u8]>,
              O: AsMut<[u8]>;
}
<commit_msg>fix(crypto::stream): publicize Stream{Encrypt,Decrypt}<commit_after>#[cfg(feature = "chacha20")] pub mod chacha20;

pub trait StreamEncrypt {
    fn encrypt_stream<I, O>(&mut self, input: I, output: O)
        where I: AsRef<[u8]>,
              O: AsMut<[u8]>;
}

pub trait StreamDecrypt {
    fn decrypt_stream<I, O>(&mut self, input: I, output: O)
        where I: AsRef<[u8]>,
              O: AsMut<[u8]>;
}
<|endoftext|>


<commit_before>use support::assert_bind_eq;

#[test]
fn ptr_to_array() {
    assert_bind_eq(Default::default(), "headers/decl_ptr_to_array.h", "
        extern \"C\" {
            pub static mut foo: [i32; 1usize];
        }
    ");
}
<commit_msg>Fix text_decl test<commit_after>use support::assert_bind_eq;

#[test]
fn ptr_to_array() {
    assert_bind_eq(Default::default(), "headers/decl_ptr_to_array.h", "
        extern \"C\" {
            pub static mut foo: [::std::os::raw::c_int; 1usize];
        }
    ");
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
use quote::quote;
use std::iter::FromIterator;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let tokens = quote!(..100u32);
    snapshot!(tokens as Expr, @r###"
    Expr::Range {
        limits: HalfOpen,
        to: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "###);

    let tokens = quote!(..100u32);
    snapshot!(tokens as ExprRange, @r###"
    ExprRange {
        limits: HalfOpen,
        to: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "###);
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let tokens = quote!(fut.await);

    snapshot!(tokens as Expr, @r###"
    Expr::Await {
        base: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "fut",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);
}

#[test]
fn test_macro_variable_func() {
    // mimics the token stream corresponding to `$fn()`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { f })),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Call {
        func: Expr::Group {
            expr: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);

    let tokens = TokenStream::from_iter(vec![
        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
        TokenTree::Group(Group::new(Delimiter::Bracket, quote! { outside })),
        TokenTree::Group(Group::new(Delimiter::None, quote! { #[inside] f })),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Call {
        attrs: [
            Attribute {
                style: Outer,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "outside",
                            arguments: None,
                        },
                    ],
                },
                tokens: TokenStream(``),
            },
        ],
        func: Expr::Group {
            expr: Expr::Path {
                attrs: [
                    Attribute {
                        style: Outer,
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "inside",
                                    arguments: None,
                                },
                            ],
                        },
                        tokens: TokenStream(``),
                    },
                ],
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);
}

#[test]
fn test_macro_variable_macro() {
    // mimics the token stream corresponding to `$macro!()`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { m })),
        TokenTree::Punct(Punct::new('!', Spacing::Alone)),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Macro {
        mac: Macro {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "m",
                        arguments: None,
                    },
                ],
            },
            delimiter: Paren,
            tokens: TokenStream(``),
        },
    }
    "###);
}

#[test]
fn test_macro_variable_struct() {
    // mimics the token stream corresponding to `$struct {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { S })),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Struct {
        path: Path {
            segments: [
                PathSegment {
                    ident: "S",
                    arguments: None,
                },
            ],
        },
    }
    "###);
}

#[test]
fn test_macro_variable_match_arm() {
    // mimics the token stream corresponding to `match v { _ => $expr }`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Ident(Ident::new("match", Span::call_site())),
        TokenTree::Ident(Ident::new("v", Span::call_site())),
        TokenTree::Group(Group::new(
            Delimiter::Brace,
            TokenStream::from_iter(vec![
                TokenTree::Punct(Punct::new('_', Spacing::Alone)),
                TokenTree::Punct(Punct::new('=', Spacing::Joint)),
                TokenTree::Punct(Punct::new('>', Spacing::Alone)),
                TokenTree::Group(Group::new(Delimiter::None, quote! { #[a] () })),
            ]),
        )),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Match {
        expr: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "v",
                        arguments: None,
                    },
                ],
            },
        },
        arms: [
            Arm {
                pat: Pat::Wild,
                body: Expr::Group {
                    expr: Expr::Tuple {
                        attrs: [
                            Attribute {
                                style: Outer,
                                path: Path {
                                    segments: [
                                        PathSegment {
                                            ident: "a",
                                            arguments: None,
                                        },
                                    ],
                                },
                                tokens: TokenStream(``),
                            },
                        ],
                    },
                },
            },
        ],
    }
    "###);
}
<commit_msg>Add test for tuple.0.0<commit_after>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
use quote::quote;
use std::iter::FromIterator;
use syn::{Expr, ExprRange};

#[test]
fn test_expr_parse() {
    let tokens = quote!(..100u32);
    snapshot!(tokens as Expr, @r###"
    Expr::Range {
        limits: HalfOpen,
        to: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "###);

    let tokens = quote!(..100u32);
    snapshot!(tokens as ExprRange, @r###"
    ExprRange {
        limits: HalfOpen,
        to: Some(Expr::Lit {
            lit: 100u32,
        }),
    }
    "###);
}

#[test]
fn test_await() {
    // Must not parse as Expr::Field.
    let tokens = quote!(fut.await);

    snapshot!(tokens as Expr, @r###"
    Expr::Await {
        base: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "fut",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);
}

#[test]
#[ignore] // https://github.com/dtolnay/syn/issues/844
fn test_tuple_multi_index() {
    let input = "tuple.0.0";
    snapshot!(input as Expr, @r###"
    Expr::Field {
        base: Expr::Field {
            base: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "tuple",
                            arguments: None,
                        },
                    ],
                },
            },
            member: Unnamed(Index {
                index: 0,
            }),
        },
        member: Unnamed(Index {
            index: 0,
        }),
    }
    "###);

    let tokens = quote!(tuple.0.0);
    snapshot!(tokens as Expr, @r###"
    Expr::Field {
        base: Expr::Field {
            base: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "tuple",
                            arguments: None,
                        },
                    ],
                },
            },
            member: Unnamed(Index {
                index: 0,
            }),
        },
        member: Unnamed(Index {
            index: 0,
        }),
    }
    "###);
}

#[test]
fn test_macro_variable_func() {
    // mimics the token stream corresponding to `$fn()`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { f })),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Call {
        func: Expr::Group {
            expr: Expr::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);

    let tokens = TokenStream::from_iter(vec![
        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
        TokenTree::Group(Group::new(Delimiter::Bracket, quote! { outside })),
        TokenTree::Group(Group::new(Delimiter::None, quote! { #[inside] f })),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Call {
        attrs: [
            Attribute {
                style: Outer,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "outside",
                            arguments: None,
                        },
                    ],
                },
                tokens: TokenStream(``),
            },
        ],
        func: Expr::Group {
            expr: Expr::Path {
                attrs: [
                    Attribute {
                        style: Outer,
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "inside",
                                    arguments: None,
                                },
                            ],
                        },
                        tokens: TokenStream(``),
                    },
                ],
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "f",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);
}

#[test]
fn test_macro_variable_macro() {
    // mimics the token stream corresponding to `$macro!()`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { m })),
        TokenTree::Punct(Punct::new('!', Spacing::Alone)),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Macro {
        mac: Macro {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "m",
                        arguments: None,
                    },
                ],
            },
            delimiter: Paren,
            tokens: TokenStream(``),
        },
    }
    "###);
}

#[test]
fn test_macro_variable_struct() {
    // mimics the token stream corresponding to `$struct {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { S })),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Struct {
        path: Path {
            segments: [
                PathSegment {
                    ident: "S",
                    arguments: None,
                },
            ],
        },
    }
    "###);
}

#[test]
fn test_macro_variable_match_arm() {
    // mimics the token stream corresponding to `match v { _ => $expr }`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Ident(Ident::new("match", Span::call_site())),
        TokenTree::Ident(Ident::new("v", Span::call_site())),
        TokenTree::Group(Group::new(
            Delimiter::Brace,
            TokenStream::from_iter(vec![
                TokenTree::Punct(Punct::new('_', Spacing::Alone)),
                TokenTree::Punct(Punct::new('=', Spacing::Joint)),
                TokenTree::Punct(Punct::new('>', Spacing::Alone)),
                TokenTree::Group(Group::new(Delimiter::None, quote! { #[a] () })),
            ]),
        )),
    ]);

    snapshot!(tokens as Expr, @r###"
    Expr::Match {
        expr: Expr::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "v",
                        arguments: None,
                    },
                ],
            },
        },
        arms: [
            Arm {
                pat: Pat::Wild,
                body: Expr::Group {
                    expr: Expr::Tuple {
                        attrs: [
                            Attribute {
                                style: Outer,
                                path: Path {
                                    segments: [
                                        PathSegment {
                                            ident: "a",
                                            arguments: None,
                                        },
                                    ],
                                },
                                tokens: TokenStream(``),
                            },
                        ],
                    },
                },
            },
        ],
    }
    "###);
}
<|endoftext|>


<commit_before><commit_msg>an empy message<commit_after><|endoftext|>


<commit_before><commit_msg>sort out engine argument types<commit_after><|endoftext|>


<commit_before>#[macro_use]
extern crate interpreter;

#[test]
fn reassign_different_type() {
    run_test!(
        should_warn(typecheck)
        => r"
            x = 1;
            x = true;
        ");
}

#[test]
fn shadow_function() {
    run_test!(
        should_warn(typecheck)
        => r"
            fn x { x }
            fn x { x*2 }
        ");
}

#[test]
fn call_non_function() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = 1;
            y = x(1, 2, 3);
        ");
}

#[test]
fn wrong_num_arguments() {
    run_test!(
        should_fail(typecheck)
        => r"
            f x, y=1 { x+y }
            x = f(1, 2, 3);
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            f x, y=1 { x+y }
            x = f();
        ");
}

#[test]
fn misspelled_argument() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            x = fn(y=5);
        ");
}

#[test]
fn ambiguous_recursion() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { fn(x) }
            y = fn(1);
        ");
}

#[test]
fn wrong_arg_type() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            x = fn(1);
            y = fn(true);
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            z = fn;
            x = z(1);
            y = fn(true);
        ");
}

#[test]
fn wrong_block_type() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = {
                true;
                false;
            };
        ");
}

#[test]
fn non_boolean_condition() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = true if 1 else false;
        ");
}

#[test]
fn reassignment() {
    run_test!(
        should_pass(typecheck)
        => r"
            x = true;
            y = x ^^ true;
            x = 1;
            y = x * 5;
        ");
    run_test!(
        should_pass(typecheck)
        => r"
            x = true;
            a { x ^^ true }
            x = 1;
            b { x * 2 }
            w = b();
            z = a();
        ");
}

#[test]
fn variable_not_in_scope() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = a;
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn {
                a = 5;
            }
            x = a;
        ");
}

#[test]
fn mismatched_conditional_types() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = 1 if true else false;
        ");
}

#[test]
fn wrong_type_numerical_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 + true;
        ");
}

#[test]
fn wrong_type_comparison_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 + true;
        ");
}

#[test]
fn wrong_type_equality_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 == true;
        ");
}

#[test]
fn wrong_type_boolean_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 && true;
        ");
}

#[test]
fn wrong_type_prefix_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = -true;
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            a = !5;
        ");
}

#[test]
fn internal_block_shadowing_captured_vars() {
    run_test!(
        should_pass(typecheck)
        => r"
            x = 5;
            c = \{x+5};
            z = {
                x = true;
                c();
            }
        ");
}

#[test]
fn default_args() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            a = fn(1);
            a = fn(1, 2);
            a = fn(1, x=2);
        ");
}

#[test]
fn partial_application() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{x=2};
            a = fn'(1);
            a = fn'(1, 2);
            a = fn'(1, x=2);
        ");
}

#[test]
fn implicit_call() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            x = 1;
            y = false;
            z = fn[];
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y { x + y }
            x = 1;
            y = false;
            z = fn[];
        ");
}

#[test]
fn unbind_expression() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            y = false;
            z = fn'[];
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            z = fn'[];
        ");
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            y = 2;
            z = fn'[];
        ");
}

#[test]
fn layered_implicit_calls() {
    run_test!(
        should_pass(typecheck)
        => r"
            a x { x^2 }
            b y { y[] }
            c z { z(b) }
            z = c[b, x=4];
        ");
}
<commit_msg>fix typo<commit_after>#[macro_use]
extern crate interpreter;

#[test]
fn reassign_different_type() {
    run_test!(
        should_warn(typecheck)
        => r"
            x = 1;
            x = true;
        ");
}

#[test]
fn shadow_function() {
    run_test!(
        should_warn(typecheck)
        => r"
            fn x { x }
            fn x { x*2 }
        ");
}

#[test]
fn call_non_function() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = 1;
            y = x(1, 2, 3);
        ");
}

#[test]
fn wrong_num_arguments() {
    run_test!(
        should_fail(typecheck)
        => r"
            f x, y=1 { x+y }
            x = f(1, 2, 3);
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            f x, y=1 { x+y }
            x = f();
        ");
}

#[test]
fn misspelled_argument() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            x = fn(y=5);
        ");
}

#[test]
fn ambiguous_recursion() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { fn(x) }
            y = fn(1);
        ");
}

#[test]
fn wrong_arg_type() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            x = fn(1);
            y = fn(true);
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x { x }
            z = fn;
            x = z(1);
            y = fn(true);
        ");
}

#[test]
fn wrong_block_type() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = {
                true;
                false;
            };
        ");
}

#[test]
fn non_boolean_condition() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = true if 1 else false;
        ");
}

#[test]
fn reassignment() {
    run_test!(
        should_pass(typecheck)
        => r"
            x = true;
            y = x ^^ true;
            x = 1;
            y = x * 5;
        ");
    run_test!(
        should_pass(typecheck)
        => r"
            x = true;
            a { x ^^ true }
            x = 1;
            b { x * 2 }
            w = b();
            z = a();
        ");
}

#[test]
fn variable_not_in_scope() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = a;
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn {
                a = 5;
            }
            x = a;
        ");
}

#[test]
fn mismatched_conditional_types() {
    run_test!(
        should_fail(typecheck)
        => r"
            x = 1 if true else false;
        ");
}

#[test]
fn wrong_type_numerical_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 + true;
        ");
}

#[test]
fn wrong_type_comparison_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 + true;
        ");
}

#[test]
fn wrong_type_equality_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 == true;
        ");
}

#[test]
fn wrong_type_boolean_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = 5 && true;
        ");
}

#[test]
fn wrong_type_prefix_op() {
    run_test!(
        should_fail(typecheck)
        => r"
            a = -true;
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            a = !5;
        ");
}

#[test]
fn internal_block_shadowing_captured_vars() {
    run_test!(
        should_pass(typecheck)
        => r"
            x = 5;
            c = \{x+5};
            z = {
                x = true;
                c();
            }
        ");
}

#[test]
fn default_args() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            a = fn(1);
            a = fn(1, 2);
            a = fn(1, x=2);
        ");
}

#[test]
fn partial_application() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{x=2};
            a = fn'(1);
            a = fn'(1, 2);
            a = fn'(1, x=2);
        ");
}

#[test]
fn implicit_call() {
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            x = 1;
            y = false;
            z = fn[];
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y { x + y }
            x = 1;
            y = false;
            z = fn[];
        ");
}

#[test]
fn unbind_expression() {
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            y = false;
            z = fn'[];
        ");
    run_test!(
        should_fail(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            z = fn'[];
        ");
    run_test!(
        should_pass(typecheck)
        => r"
            fn x, y=3 { x + y }
            fn' = fn{y=};
            x = 1;
            y = 2;
            z = fn'[];
        ");
}

#[test]
fn layered_implicit_calls() {
    run_test!(
        should_pass(typecheck)
        => r"
            a x { x^2 }
            b y { y[] }
            c z { z(a) }
            z = c[b, x=4];
        ");
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::env;
use std::fs::read_dir;
use std::io::Write;
use std::iter::FromIterator;
use std::os::unix::fs::PermissionsExt;
use std::path::{is_separator, MAIN_SEPARATOR};
use std::sync::Arc;

use linefeed::complete::{Completer, Completion, Suffix};
use linefeed::terminal::Terminal;
use linefeed::Prompter;

use parsers;
use shell;
use tools;

pub struct BinCompleter {
    pub sh: Arc<shell::Shell>,
}
pub struct CdCompleter;
pub struct PathCompleter;

impl<Term: Terminal> Completer<Term> for BinCompleter {
    fn complete(
        &self,
        word: &str,
        _reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        // TODO: use RC::into_raw() instead
        let sh = Arc::try_unwrap(self.sh.clone());
        match sh {
            Ok(x) => Some(complete_bin(&x, word)),
            Err(e) => Some(complete_bin(&e, word)),
        }
    }
}

impl<Term: Terminal> Completer<Term> for PathCompleter {
    fn complete(
        &self,
        _word: &str,
        reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        let buffer = reader.buffer();
        Some(complete_path(buffer, false))
    }
}

impl<Term: Terminal> Completer<Term> for CdCompleter {
    fn complete(
        &self,
        _word: &str,
        reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        let buffer = reader.buffer();
        Some(complete_path(buffer, true))
    }
}

fn ends_with_space(line: &str) -> bool {
    let mut found_bs = false;
    let mut found_space = false;
    let mut with_quote = false;
    let mut ch_quote = '\0';
    for c in line.chars() {
        if c == '\\' {
            found_bs = true;
            continue;
        }

        if !with_quote && !found_bs && (c == '"' || c == '\'') {
            with_quote = true;
            ch_quote = c;
        } else if with_quote && !found_bs && ch_quote == c {
            with_quote = false;
        }

        if c == ' ' && !found_bs && !with_quote {
            found_space = true;
        }
        if found_space && c != ' ' {
            found_space = false;
        }
        found_bs = false;
    }
    found_space
}

/// Returns a sorted list of paths whose prefix matches the given path.
pub fn complete_path(buffer: &str, for_dir: bool) -> Vec<Completion> {
    let mut res = Vec::new();
    let mut path_sep = String::new();
    let mut path = String::new();

    if !ends_with_space(buffer) {
        let tokens = parsers::parser_line::cmd_to_tokens(buffer);
        if tokens.is_empty() {
            return res;
        }

        let (ref _path_sep, ref _path) = tokens[tokens.len() - 1];
        path = _path.clone();
        path_sep = _path_sep.clone();
    }

    let (_dir_orig, _) = split_path(&path);
    let dir_orig = if let Some(_dir) = _dir_orig { _dir } else { "" };
    let mut path_extended = path.clone();
    if shell::needs_expand_home(&path_extended) {
        shell::expand_home_string(&mut path_extended)
    }
    let (_dir_lookup, file_name) = split_path(&path_extended);
    let dir_lookup = _dir_lookup.unwrap_or(".");
    if let Ok(entries) = read_dir(dir_lookup) {
        for entry in entries {
            if let Ok(entry) = entry {
                let pathbuf = entry.path();
                let is_dir = pathbuf.is_dir();
                if for_dir && !is_dir {
                    continue;
                }

                let entry_name = entry.file_name();
                // TODO: Deal with non-UTF8 paths in some way
                if let Ok(_path) = entry_name.into_string() {
                    if _path.starts_with(file_name) {
                        let (name, display) = if dir_orig != "" {
                            (
                                format!("{}{}{}", dir_orig, MAIN_SEPARATOR, _path),
                                Some(_path),
                            )
                        } else {
                            (_path, None)
                        };
                        let mut name = str::replace(name.as_str(), "//", "/");
                        if path_sep.is_empty() {
                            name = str::replace(&name, " ", "\\ ");
                            name = str::replace(&name, "\"", "\\\"");
                            name = str::replace(&name, "\'", "\\\'");
                            name = str::replace(&name, "*", "\\*");
                        }
                        let suffix = if is_dir {
                            Suffix::Some(MAIN_SEPARATOR)
                        } else {
                            Suffix::Default
                        };
                        if !path_sep.is_empty() {
                            name = tools::wrap_sep_string(&path_sep, &name);
                        }
                        res.push(Completion {
                            completion: name,
                            display,
                            suffix,
                        });
                    }
                }
            }
        }
    }
    res
}

fn split_path(path: &str) -> (Option<&str>, &str) {
    match path.rfind(is_separator) {
        Some(pos) => (Some(&path[..=pos]), &path[pos + 1..]),
        None => (None, path),
    }
}

/// Returns a sorted list of paths whose prefix matches the given path.
fn complete_bin(sh: &shell::Shell, path: &str) -> Vec<Completion> {
    let mut res = Vec::new();
    let (_, fname) = split_path(path);
    let env_path;
    match env::var("PATH") {
        Ok(x) => env_path = x,
        Err(e) => {
            println_stderr!("cicada: env error when complete_bin: {:?}", e);
            return res;
        }
    }

    // handle alias and builtins
    for alias in sh.alias.keys() {
        if !alias.starts_with(fname) {
            continue;
        }
        res.push(Completion {
            completion: alias.to_owned(),
            display: None,
            suffix: Suffix::Default,
        });
    }
    let builtins = vec![
        "cd", "cinfo", "exec", "exit", "export", "fg", "history", "jobs", "fg", "vox",
    ];
    for item in &builtins {
        if !item.starts_with(fname) {
            continue;
        }
        res.push(Completion {
            completion: item.to_string(),
            display: None,
            suffix: Suffix::Default,
        });
    }

    let vec_path: Vec<&str> = env_path.split(':').collect();
    let path_list: HashSet<&str> = HashSet::from_iter(vec_path.iter().cloned());

    let mut checker: HashSet<String> = HashSet::new();
    for p in &path_list {
        if let Ok(list) = read_dir(p) {
            for entry in list {
                if let Ok(entry) = entry {
                    if let Ok(name) = entry.file_name().into_string() {
                        if name.starts_with(fname) {
                            let _mode;
                            match entry.metadata() {
                                Ok(x) => _mode = x,
                                Err(e) => {
                                    println_stderr!("cicada: metadata error: {:?}", e);
                                    continue;
                                }
                            }
                            let mode = _mode.permissions().mode();
                            if mode & 0o111 == 0 {
                                // not binary
                                continue;
                            }
                            if checker.contains(&name) {
                                continue;
                            }

                            let display = None;
                            let suffix = Suffix::Default;
                            checker.insert(name.clone());
                            res.push(Completion {
                                completion: name,
                                display,
                                suffix,
                            });
                        }
                    }
                }
            }
        }
    }
    res
}

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

    #[test]
    fn test_split_path() {
        assert_eq!(split_path(""), (None, ""));
        assert_eq!(split_path(""), (None, ""));
    }

    #[test]
    fn test_ends_with_space() {
        assert!(ends_with_space("ls "));
        assert!(ends_with_space("ls a "));
        assert!(ends_with_space("  ls   foo "));
        assert!(ends_with_space("\"ls\" "));
        assert!(ends_with_space("\"ls\" a "));

        assert!(!ends_with_space("ls a"));

        assert!(!ends_with_space("ls \"a "));
        assert!(!ends_with_space("ls \"a b"));
        assert!(!ends_with_space("ls \"a b "));
        assert!(!ends_with_space("ls \'a "));
        assert!(!ends_with_space("ls \'a b"));
        assert!(!ends_with_space("ls \'a b "));

        assert!(!ends_with_space("\"ls\" \"a "));
        assert!(!ends_with_space("\"ls\" \"a b"));

        assert!(!ends_with_space("ls a\\ "));
        assert!(!ends_with_space("ls a\\ b"));
        assert!(!ends_with_space("  ls   a\\ b\\ c"));

    }
}
<commit_msg>minor updates on path completion<commit_after>use std::collections::HashSet;
use std::env;
use std::fs::read_dir;
use std::io::Write;
use std::iter::FromIterator;
use std::os::unix::fs::PermissionsExt;
use std::path::{is_separator, MAIN_SEPARATOR};
use std::sync::Arc;

use linefeed::complete::{Completer, Completion, Suffix};
use linefeed::terminal::Terminal;
use linefeed::Prompter;

use parsers;
use shell;
use tools;

pub struct BinCompleter {
    pub sh: Arc<shell::Shell>,
}
pub struct CdCompleter;
pub struct PathCompleter;

impl<Term: Terminal> Completer<Term> for BinCompleter {
    fn complete(
        &self,
        word: &str,
        _reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        // TODO: use RC::into_raw() instead
        let sh = Arc::try_unwrap(self.sh.clone());
        match sh {
            Ok(x) => Some(complete_bin(&x, word)),
            Err(e) => Some(complete_bin(&e, word)),
        }
    }
}

impl<Term: Terminal> Completer<Term> for PathCompleter {
    fn complete(
        &self,
        _word: &str,
        reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        let buffer = reader.buffer();
        Some(complete_path(buffer, false))
    }
}

impl<Term: Terminal> Completer<Term> for CdCompleter {
    fn complete(
        &self,
        _word: &str,
        reader: &Prompter<Term>,
        _start: usize,
        _end: usize,
    ) -> Option<Vec<Completion>> {
        let buffer = reader.buffer();
        Some(complete_path(buffer, true))
    }
}

fn ends_with_space(line: &str) -> bool {
    let mut found_bs = false;
    let mut found_space = false;
    let mut with_quote = false;
    let mut ch_quote = '\0';
    for c in line.chars() {
        if c == '\\' {
            found_bs = true;
            continue;
        }

        if !with_quote && !found_bs && (c == '"' || c == '\'') {
            with_quote = true;
            ch_quote = c;
        } else if with_quote && !found_bs && ch_quote == c {
            with_quote = false;
        }

        if c == ' ' && !found_bs && !with_quote {
            found_space = true;
        }
        if found_space && c != ' ' {
            found_space = false;
        }
        found_bs = false;
    }
    found_space
}

/// Returns a sorted list of paths whose prefix matches the given path.
pub fn complete_path(buffer: &str, for_dir: bool) -> Vec<Completion> {
    let mut res = Vec::new();
    let mut path_sep = String::new();
    let mut path = String::new();

    if !ends_with_space(buffer) {
        let tokens = parsers::parser_line::cmd_to_tokens(buffer);
        if tokens.is_empty() {
            return res;
        }

        let (ref _path_sep, ref _path) = tokens[tokens.len() - 1];
        path = _path.clone();
        path_sep = _path_sep.clone();
    }

    let (_dir_orig, _) = split_path(&path);
    let dir_orig = if let Some(_dir) = _dir_orig { _dir } else { "" };
    let mut path_extended = path.clone();
    if shell::needs_expand_home(&path_extended) {
        shell::expand_home_string(&mut path_extended)
    }
    let (_dir_lookup, file_name) = split_path(&path_extended);
    let dir_lookup = _dir_lookup.unwrap_or(".");
    if let Ok(entries) = read_dir(dir_lookup) {
        for entry in entries {
            if let Ok(entry) = entry {
                let pathbuf = entry.path();
                let is_dir = pathbuf.is_dir();
                if for_dir && !is_dir {
                    continue;
                }

                let entry_name = entry.file_name();
                // TODO: Deal with non-UTF8 paths in some way
                if let Ok(_path) = entry_name.into_string() {
                    if _path.starts_with(file_name) {
                        let (name, display) = if dir_orig != "" {
                            (
                                format!("{}{}{}", dir_orig, MAIN_SEPARATOR, _path),
                                Some(_path),
                            )
                        } else {
                            (_path, None)
                        };
                        let mut name = str::replace(name.as_str(), "//", "/");
                        if path_sep.is_empty() {
                            name = str::replace(&name, " ", "\\ ");
                            name = str::replace(&name, "\"", "\\\"");
                            name = str::replace(&name, "\'", "\\\'");
                            name = str::replace(&name, "*", "\\*");
                        }
                        let mut quoted = false;
                        if !path_sep.is_empty() {
                            name = tools::wrap_sep_string(&path_sep, &name);
                            quoted = true;
                        }
                        let suffix = if is_dir {
                            if quoted {
                                name = name.trim_end_matches(&path_sep).to_string();
                            }
                            Suffix::Some(MAIN_SEPARATOR)
                        } else {
                            Suffix::Default
                        };
                        res.push(Completion {
                            completion: name,
                            display,
                            suffix,
                        });
                    }
                }
            }
        }
    }
    res
}

fn split_path(path: &str) -> (Option<&str>, &str) {
    match path.rfind(is_separator) {
        Some(pos) => (Some(&path[..=pos]), &path[pos + 1..]),
        None => (None, path),
    }
}

/// Returns a sorted list of paths whose prefix matches the given path.
fn complete_bin(sh: &shell::Shell, path: &str) -> Vec<Completion> {
    let mut res = Vec::new();
    let (_, fname) = split_path(path);
    let env_path;
    match env::var("PATH") {
        Ok(x) => env_path = x,
        Err(e) => {
            println_stderr!("cicada: env error when complete_bin: {:?}", e);
            return res;
        }
    }

    // handle alias and builtins
    for alias in sh.alias.keys() {
        if !alias.starts_with(fname) {
            continue;
        }
        res.push(Completion {
            completion: alias.to_owned(),
            display: None,
            suffix: Suffix::Default,
        });
    }
    let builtins = vec![
        "cd", "cinfo", "exec", "exit", "export", "fg", "history", "jobs", "fg", "vox",
    ];
    for item in &builtins {
        if !item.starts_with(fname) {
            continue;
        }
        res.push(Completion {
            completion: item.to_string(),
            display: None,
            suffix: Suffix::Default,
        });
    }

    let vec_path: Vec<&str> = env_path.split(':').collect();
    let path_list: HashSet<&str> = HashSet::from_iter(vec_path.iter().cloned());

    let mut checker: HashSet<String> = HashSet::new();
    for p in &path_list {
        if let Ok(list) = read_dir(p) {
            for entry in list {
                if let Ok(entry) = entry {
                    if let Ok(name) = entry.file_name().into_string() {
                        if name.starts_with(fname) {
                            let _mode;
                            match entry.metadata() {
                                Ok(x) => _mode = x,
                                Err(e) => {
                                    println_stderr!("cicada: metadata error: {:?}", e);
                                    continue;
                                }
                            }
                            let mode = _mode.permissions().mode();
                            if mode & 0o111 == 0 {
                                // not binary
                                continue;
                            }
                            if checker.contains(&name) {
                                continue;
                            }

                            let display = None;
                            let suffix = Suffix::Default;
                            checker.insert(name.clone());
                            res.push(Completion {
                                completion: name,
                                display,
                                suffix,
                            });
                        }
                    }
                }
            }
        }
    }
    res
}

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

    #[test]
    fn test_split_path() {
        assert_eq!(split_path(""), (None, ""));
        assert_eq!(split_path(""), (None, ""));
    }

    #[test]
    fn test_ends_with_space() {
        assert!(ends_with_space("ls "));
        assert!(ends_with_space("ls a "));
        assert!(ends_with_space("  ls   foo "));
        assert!(ends_with_space("\"ls\" "));
        assert!(ends_with_space("\"ls\" a "));

        assert!(!ends_with_space("ls a"));

        assert!(!ends_with_space("ls \"a "));
        assert!(!ends_with_space("ls \"a b"));
        assert!(!ends_with_space("ls \"a b "));
        assert!(!ends_with_space("ls \'a "));
        assert!(!ends_with_space("ls \'a b"));
        assert!(!ends_with_space("ls \'a b "));

        assert!(!ends_with_space("\"ls\" \"a "));
        assert!(!ends_with_space("\"ls\" \"a b"));

        assert!(!ends_with_space("ls a\\ "));
        assert!(!ends_with_space("ls a\\ b"));
        assert!(!ends_with_space("  ls   a\\ b\\ c"));

    }
}
<|endoftext|>


<commit_before><commit_msg>fix typo<commit_after><|endoftext|>


<commit_before><commit_msg>feature: Decrease delay when waiting for app to start<commit_after><|endoftext|>


<commit_before><commit_msg>vhost_kern: vdpa: Override valid()<commit_after><|endoftext|>


<commit_before>use std::c_str::CString;
use std::c_vec::CVec;
use std::kinds::marker::NoCopy;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_int, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

#[repr(C)]
pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	nocopy: NoCopy,
}

pub struct Method {
	nocopy: NoCopy,
}

pub struct Class {
	nocopy: NoCopy,
}

pub struct Object {
	nocopy: NoCopy,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
	pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
	pub fn objc_getClass(name: *const c_char) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
	pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
	pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn return_type(&self) -> CString {
		unsafe {
			let encoding = method_copyReturnType(self);
			CString::new(encoding as *const _, true)
		}
	}

	pub fn argument_type(&self, index: uint) -> CString {
		unsafe {
			let encoding = method_copyArgumentType(self, index as c_uint);
			CString::new(encoding as *const _, true)
		}
	}

	pub fn arguments_count(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}

	pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
		method_exchangeImplementations(self, other);
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		name.with_c_str(|name| unsafe {
			objc_getClass(name).as_ref()
		})
	}

	pub fn classes() -> CVec<&'static Class> {
		unsafe {
			let mut count: c_uint = 0;
			let classes = objc_copyClassList(&mut count);
			CVec::new_with_dtor(classes as *mut _, count as uint, proc() {
				libc::free(classes as *mut c_void);
			})
		}
	}

	pub fn classes_count() -> uint {
		unsafe {
			objc_getClassList(RawPtr::null(), 0) as uint
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		unsafe {
			class_getInstanceMethod(self, sel).as_ref()
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name).as_ref()
		})
	}

	pub fn instance_methods(&self) -> CVec<&Method> {
		unsafe {
			let mut count: c_uint = 0;
			let methods = class_copyMethodList(self, &mut count);
			CVec::new_with_dtor(methods as *mut _, count as uint, proc() {
				libc::free(methods as *mut c_void);
			})
		}

	}

	pub fn instance_variables(&self) -> CVec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			CVec::new_with_dtor(ivars as *mut _, count as uint, proc() {
				libc::free(ivars as *mut c_void);
			})
		}
	}
}

impl PartialEq for Class {
	fn eq(&self, other: &Class) -> bool {
		self as *const Class == other as *const Class
	}
}

impl Eq for Class { }

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => panic!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => panic!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

#[cfg(test)]
mod tests {
	use std::mem;
	use super::{Class, Sel};

	#[test]
	fn test_ivar() {
		let cls = Class::get("NSObject").unwrap();
		let ivar = cls.instance_variable("isa").unwrap();
		assert!(ivar.name() == "isa");
		assert!(ivar.type_encoding() == "#");
		assert!(ivar.offset() == 0);

		let ivars = cls.instance_variables();
		assert!(ivars.len() > 0);
	}

	#[test]
	fn test_method() {
		let cls = Class::get("NSObject").unwrap();
		let sel = Sel::register("description");
		let method = cls.instance_method(sel).unwrap();
		assert!(method.name().name() == "description");
		assert!(method.type_encoding() != "");
		assert!(method.arguments_count() == 2);
		assert!(method.return_type().as_bytes_no_nul() == "@".as_bytes());
		assert!(method.argument_type(1).as_bytes_no_nul() == ":".as_bytes());

		let methods = cls.instance_methods();
		assert!(methods.len() > 0);
	}

	#[test]
	fn test_class() {
		let cls = Class::get("NSObject").unwrap();
		assert!(cls.name() == "NSObject");
		assert!(cls.instance_size() == mem::size_of::<*const Class>());
	}

	#[test]
	fn test_classes() {
		let classes_count = Class::classes_count();
		assert!(classes_count > 0);

		let classes = Class::classes();
		assert!(classes.len() == classes_count);
	}

	#[test]
	fn test_object() {
		let cls = Class::get("NSObject").unwrap();
		let obj = unsafe {
			let obj = msg_send![cls alloc];
			let obj = msg_send![obj init];
			&*obj
		};
		assert!(obj.class() == cls);
		unsafe {
			msg_send![obj release];
		}
	}
}
<commit_msg>Fixed Method's argument_type to return an Option.<commit_after>use std::c_str::CString;
use std::c_vec::CVec;
use std::kinds::marker::NoCopy;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_int, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

#[repr(C)]
pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	nocopy: NoCopy,
}

pub struct Method {
	nocopy: NoCopy,
}

pub struct Class {
	nocopy: NoCopy,
}

pub struct Object {
	nocopy: NoCopy,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
	pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
	pub fn objc_getClass(name: *const c_char) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
	pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
	pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn return_type(&self) -> CString {
		unsafe {
			let encoding = method_copyReturnType(self);
			CString::new(encoding as *const _, true)
		}
	}

	pub fn argument_type(&self, index: uint) -> Option<CString> {
		unsafe {
			let encoding = method_copyArgumentType(self, index as c_uint);
			if encoding.is_null() {
				None
			} else {
				Some(CString::new(encoding as *const _, true))
			}
		}
	}

	pub fn arguments_count(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}

	pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
		method_exchangeImplementations(self, other);
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		name.with_c_str(|name| unsafe {
			objc_getClass(name).as_ref()
		})
	}

	pub fn classes() -> CVec<&'static Class> {
		unsafe {
			let mut count: c_uint = 0;
			let classes = objc_copyClassList(&mut count);
			CVec::new_with_dtor(classes as *mut _, count as uint, proc() {
				libc::free(classes as *mut c_void);
			})
		}
	}

	pub fn classes_count() -> uint {
		unsafe {
			objc_getClassList(RawPtr::null(), 0) as uint
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		unsafe {
			class_getInstanceMethod(self, sel).as_ref()
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name).as_ref()
		})
	}

	pub fn instance_methods(&self) -> CVec<&Method> {
		unsafe {
			let mut count: c_uint = 0;
			let methods = class_copyMethodList(self, &mut count);
			CVec::new_with_dtor(methods as *mut _, count as uint, proc() {
				libc::free(methods as *mut c_void);
			})
		}

	}

	pub fn instance_variables(&self) -> CVec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			CVec::new_with_dtor(ivars as *mut _, count as uint, proc() {
				libc::free(ivars as *mut c_void);
			})
		}
	}
}

impl PartialEq for Class {
	fn eq(&self, other: &Class) -> bool {
		self as *const Class == other as *const Class
	}
}

impl Eq for Class { }

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => panic!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => panic!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

#[cfg(test)]
mod tests {
	use std::mem;
	use super::{Class, Sel};

	#[test]
	fn test_ivar() {
		let cls = Class::get("NSObject").unwrap();
		let ivar = cls.instance_variable("isa").unwrap();
		assert!(ivar.name() == "isa");
		assert!(ivar.type_encoding() == "#");
		assert!(ivar.offset() == 0);

		let ivars = cls.instance_variables();
		assert!(ivars.len() > 0);
	}

	#[test]
	fn test_method() {
		let cls = Class::get("NSObject").unwrap();
		let sel = Sel::register("description");
		let method = cls.instance_method(sel).unwrap();
		assert!(method.name().name() == "description");
		assert!(method.type_encoding() != "");
		assert!(method.arguments_count() == 2);
		assert!(method.return_type().as_bytes_no_nul() == "@".as_bytes());
		assert!(method.argument_type(1).unwrap().as_bytes_no_nul() ==
			":".as_bytes());

		let methods = cls.instance_methods();
		assert!(methods.len() > 0);
	}

	#[test]
	fn test_class() {
		let cls = Class::get("NSObject").unwrap();
		assert!(cls.name() == "NSObject");
		assert!(cls.instance_size() == mem::size_of::<*const Class>());
	}

	#[test]
	fn test_classes() {
		let classes_count = Class::classes_count();
		assert!(classes_count > 0);

		let classes = Class::classes();
		assert!(classes.len() == classes_count);
	}

	#[test]
	fn test_object() {
		let cls = Class::get("NSObject").unwrap();
		let obj = unsafe {
			let obj = msg_send![cls alloc];
			let obj = msg_send![obj init];
			&*obj
		};
		assert!(obj.class() == cls);
		unsafe {
			msg_send![obj release];
		}
	}
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use time;

use crate::backend::Backend;
use crate::helpers::{TaskProgress, TaskReport};
use crate::models::Service;

/// Persists a shared vector of reports to the Task store
pub fn mark_done_arc(
  backend: &Backend,
  reports_arc: &Arc<Mutex<Vec<TaskReport>>>,
) -> Result<bool, String>
{
  let reports = drain_shared_vec(reports_arc);
  if !reports.is_empty() {
    let request_time = time::get_time();
    let mut success = false;
    if let Err(e) = backend.mark_done(&reports) {
      println!("-- mark_done attempt failed: {:?}", e);
      // DB persist failed, retry
      let mut retries = 0;
      while retries < 3 {
        thread::sleep(Duration::new(2, 0)); // wait 2 seconds before retrying, in case this is latency related
        retries += 1;
        match backend.mark_done(&reports) {
          Ok(_) => {
            success = true;
            break;
          },
          Err(e) => println!("-- mark_done retry failed: {:?}", e),
        };
      }
    } else {
      success = true;
    }
    if !success {
      return Err(String::from(
        "Database ran away during mark_done persisting.",
      ));
    }
    let responded_time = time::get_time();
    let request_duration = (responded_time - request_time).num_milliseconds();
    println!(
      "finalize: reporting tasks to DB took {}ms.",
      request_duration
    );
    Ok(true)
  } else {
    Ok(false)
  }
}
/// Adds a task report to a shared report queue
pub fn push_done_queue(reports_arc: &Arc<Mutex<Vec<TaskReport>>>, report: TaskReport) {
  let mut reports = reports_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in push_done_queue"));
  if reports.len() > 10_000 {
    panic!(
      "Done queue is too large: {:?} tasks. Stop the sink!",
      reports.len()
    );
  }
  reports.push(report)
}

/// Check for, remove and return any expired tasks from the progress queue
pub fn timeout_progress_tasks<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
) -> Vec<TaskProgress> {
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in timeout_progress_tasks"));
  let now = time::get_time().sec;
  let expired_keys = progress_queue
    .iter()
    .filter(|&(_, v)| v.expected_at() < now)
    .map(|(k, _)| *k)
    .collect::<Vec<_>>();
  let mut expired_tasks = Vec::new();
  for key in expired_keys {
    match progress_queue.remove(&key) {
      None => {},
      Some(task_progress) => expired_tasks.push(task_progress),
    }
  }
  expired_tasks
}

/// Pops the next task from the progress queue
pub fn pop_progress_task<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
  taskid: i64,
) -> Option<TaskProgress>
{
  if taskid < 0 {
    // Mock ids are to be skipped
    return None;
  }
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in pop_progress_task"));
  progress_queue.remove(&taskid)
}

/// Pushes a new task on the progress queue
pub fn push_progress_task<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
  progress_task: TaskProgress,
)
{
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in push_progress_task"));
  // NOTE: This constant should be adjusted if you expect a fringe of more than 10,000 jobs
  //       I am using this as a workaround for the inability to catch thread panic!() calls.
  if progress_queue.len() > 10_000 {
    panic!(
      "Progress queue is too large: {:?} tasks. Stop the ventilator!",
      progress_queue.len()
    );
  }
  progress_queue.insert(progress_task.task.id, progress_task);
}

/// Drain a `Vec` inside an `Arc<Mutex>`
pub fn drain_shared_vec<T: Clone>(vec_arc: &Arc<Mutex<Vec<T>>>) -> Vec<T> {
  let mut vec_mutex_guard = vec_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in drain_shared_vec"));
  let fetched_vec: Vec<T> = (*vec_mutex_guard).drain(..).collect();
  fetched_vec
}

/// Memoized getter for a `Service` record from the backend
pub fn get_sync_service<S: ::std::hash::BuildHasher>(
  service_name: &str,
  services: &Arc<Mutex<HashMap<String, Option<Service>, S>>>,
  backend: &Backend,
) -> Option<Service>
{
  let mut services = services
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in get_sync_services"));
  services
    .entry(service_name.to_string())
    .or_insert_with(
      || match Service::find_by_name(service_name, &backend.connection) {
        Ok(s) => Some(s),
        _ => None,
      },
    )
    .clone()
}

/// Getter for a `Service` stored inside an `Arc<Mutex<HashMap>`, with no DB access
pub fn get_service<S: ::std::hash::BuildHasher>(
  service_name: &str,
  services: &Arc<Mutex<HashMap<String, Option<Service>, S>>>,
) -> Option<Service>
{
  let services = services
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in get_service"));
  match services.get(service_name) {
    None => None, // TODO: Handle errors
    Some(service) => service.clone(),
  }
}
<commit_msg>poison "done tasks" mutex when finalize thread panics, and panic entire dispatcher<commit_after>use std::collections::HashMap;
use std::sync::Arc;
use std::sync::{Mutex};
use std::thread;
use std::time::Duration;
use time;

use crate::backend::Backend;
use crate::helpers::{TaskProgress, TaskReport};
use crate::models::Service;

/// Persists a shared vector of reports to the Task store
pub fn mark_done_arc(
  backend: &Backend,
  reports_arc: &Arc<Mutex<Vec<TaskReport>>>,
) -> Result<bool, String>
{
  // Important: hold the mutex lock for the entirety of the mark_done process,
  // so that it gets poisoned if the DB runs away and the thread panics
  // we want the entire dispatcher to panic if this thread panics.
  let mut mutex_guard = reports_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in drain_shared_vec"));

  let reports : Vec<TaskReport> = (*mutex_guard).drain(..).collect();
  if !reports.is_empty() {
    let request_time = time::get_time();
    let mut success = false;
    if let Err(e) = backend.mark_done(&reports) {
      println!("-- mark_done attempt failed: {:?}", e);
      // DB persist failed, retry
      let mut retries = 0;
      while retries < 3 {
        thread::sleep(Duration::new(2, 0)); // wait 2 seconds before retrying, in case this is latency related
        retries += 1;
        match backend.mark_done(&reports) {
          Ok(_) => {
            success = true;
            break;
          },
          Err(e) => println!("-- mark_done retry failed: {:?}", e),
        };
      }
    } else {
      success = true;
    }
    if !success {
      return Err(String::from(
        "Database ran away during mark_done persisting.",
      ));
    }
    let responded_time = time::get_time();
    let request_duration = (responded_time - request_time).num_milliseconds();
    println!(
      "finalize: reporting tasks to DB took {}ms.",
      request_duration
    );
    Ok(true)
  } else {
    Ok(false)
  }
}
/// Adds a task report to a shared report queue
pub fn push_done_queue(reports_arc: &Arc<Mutex<Vec<TaskReport>>>, report: TaskReport) {
  let mut reports = reports_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in push_done_queue"));
  if reports.len() > 10_000 {
    panic!(
      "Done queue is too large: {:?} tasks. Stop the sink!",
      reports.len()
    );
  }
  reports.push(report)
}

/// Check for, remove and return any expired tasks from the progress queue
pub fn timeout_progress_tasks<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
) -> Vec<TaskProgress> {
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in timeout_progress_tasks"));
  let now = time::get_time().sec;
  let expired_keys = progress_queue
    .iter()
    .filter(|&(_, v)| v.expected_at() < now)
    .map(|(k, _)| *k)
    .collect::<Vec<_>>();
  let mut expired_tasks = Vec::new();
  for key in expired_keys {
    match progress_queue.remove(&key) {
      None => {},
      Some(task_progress) => expired_tasks.push(task_progress),
    }
  }
  expired_tasks
}

/// Pops the next task from the progress queue
pub fn pop_progress_task<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
  taskid: i64,
) -> Option<TaskProgress>
{
  if taskid < 0 {
    // Mock ids are to be skipped
    return None;
  }
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in pop_progress_task"));
  progress_queue.remove(&taskid)
}

/// Pushes a new task on the progress queue
pub fn push_progress_task<S: ::std::hash::BuildHasher>(
  progress_queue_arc: &Arc<Mutex<HashMap<i64, TaskProgress, S>>>,
  progress_task: TaskProgress,
)
{
  let mut progress_queue = progress_queue_arc
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in push_progress_task"));
  // NOTE: This constant should be adjusted if you expect a fringe of more than 10,000 jobs
  //       I am using this as a workaround for the inability to catch thread panic!() calls.
  if progress_queue.len() > 10_000 {
    panic!(
      "Progress queue is too large: {:?} tasks. Stop the ventilator!",
      progress_queue.len()
    );
  }
  progress_queue.insert(progress_task.task.id, progress_task);
}

/// Memoized getter for a `Service` record from the backend
pub fn get_sync_service<S: ::std::hash::BuildHasher>(
  service_name: &str,
  services: &Arc<Mutex<HashMap<String, Option<Service>, S>>>,
  backend: &Backend,
) -> Option<Service>
{
  let mut services = services
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in get_sync_services"));
  services
    .entry(service_name.to_string())
    .or_insert_with(
      || match Service::find_by_name(service_name, &backend.connection) {
        Ok(s) => Some(s),
        _ => None,
      },
    )
    .clone()
}

/// Getter for a `Service` stored inside an `Arc<Mutex<HashMap>`, with no DB access
pub fn get_service<S: ::std::hash::BuildHasher>(
  service_name: &str,
  services: &Arc<Mutex<HashMap<String, Option<Service>, S>>>,
) -> Option<Service>
{
  let services = services
    .lock()
    .unwrap_or_else(|_| panic!("Failed to obtain Mutex lock in get_service"));
  match services.get(service_name) {
    None => None, // TODO: Handle errors
    Some(service) => service.clone(),
  }
}
<|endoftext|>