<commit_before>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{self, Runtime},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(app.clone(), display_list.clone(), event_loop_proxy);

    // A single-threaded runtime for driving futures on the main thread only
    let basic_rt = runtime::Builder::new()
        .basic_scheduler()
        .build()
        .expect("unable to spawn tokio runtime to run turtle server process");

    let window_builder = {
        let drawing = basic_rt.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = basic_rt.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = basic_rt.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    thread::spawn(move || {
        let mut runtime = Runtime::new()
            .expect("unable to spawn tokio runtime to run turtle server process");

        // Spawn root task
        runtime.block_on(async {
            let conn = ServerConnection::connect_stdin().await
                .expect("unable to establish turtle server connection");
            super::serve(conn, app, display_list, event_loop).await;
        });
    });
}
<commit_msg>Need to set ControlFlow to Wait or it will keep Poll forever<commit_after>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{self, Runtime},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(app.clone(), display_list.clone(), event_loop_proxy);

    // A single-threaded runtime for driving futures on the main thread only
    let basic_rt = runtime::Builder::new()
        .basic_scheduler()
        .build()
        .expect("unable to spawn tokio runtime to run turtle server process");

    let window_builder = {
        let drawing = basic_rt.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = basic_rt.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = basic_rt.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");

            *control_flow = ControlFlow::Wait;
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    thread::spawn(move || {
        let mut runtime = Runtime::new()
            .expect("unable to spawn tokio runtime to run turtle server process");

        // Spawn root task
        runtime.block_on(async {
            let conn = ServerConnection::connect_stdin().await
                .expect("unable to establish turtle server connection");
            super::serve(conn, app, display_list, event_loop).await;
        });
    });
}
<|endoftext|>


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

use crate::code_block::CodeBlock;
use crate::errors::{CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
        };
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.to_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .env("CARGO_TARGET_DIR", &self.target_dir())
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .current_dir(self.crate_dir());
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = command.output()?;
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<commit_msg>Copy dlls by reading and writing files rather than by using std::fs:copy<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::code_block::CodeBlock;
use crate::errors::{CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    // Copy file by reading and writing instead of using std::fs::copy. The src
    // is a hard-linked file and we want to make extra sure that we end up with
    // a completely indepent copy.
    fn alt_copy(src: &Path, dest: &Path) -> Result<(), std::io::Error> {
        use std::fs::File;
        std::io::copy(&mut File::open(src)?, &mut File::create(dest)?)?;
        Ok(())
    }
    if let Err(err) = alt_copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
        };
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.to_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .env("CARGO_TARGET_DIR", &self.target_dir())
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .current_dir(self.crate_dir());
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = command.output()?;
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<|endoftext|>


<commit_before>use runtime::Runtime;
use storage::backend::StorageBackend;

use module::Module;
use module::ModuleError;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use module::bm::header::get_tags_from_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;
use ui::file::{FilePrinter, TablePrinter};
use std::vec::IntoIter;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    let url = env.matches.value_of("url").unwrap();
    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    putres.map_err(|sberr| {
        let mut err = ModuleError::new("Storage Backend Error");
        err.caused_by = Some(Box::new(sberr));
        err
    })
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let printer = TablePrinter::new(env.rt.is_verbose(), env.rt.is_debugging());
    let files   = get_filtered_files_from_backend(module, &env);

    debug!("Printing files now");
    printer.print_files(files);

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    let tags    = get_tags(env.rt, env.matches);
    let matcher = get_matcher(env.rt, env.matches);
    let id      = get_id(env.rt, env.matches);

    match id {
        Some(idstr) => {
            info!("Removing urls with id '{}'", idstr);
        }
        None => {
            match matcher {
                Some(reg) => {
                    info!("Removing urls with matcher '{}' and with tags {:?}",
                             reg.as_str(), tags);
                }
                None => {
                    info!("Listing urls with tags {:?}", tags);
                }
            }
        }
    }

    Ok(())
}

/*
 *
 * Private helpers
 *
 */

fn get_filtered_files_from_backend<'a>(module: &'a Module,
                                       env: &CommandEnv) -> IntoIter<File<'a>>
{
    let parser = Parser::new(JsonHeaderParser::new(None));
    let tags = get_tags(env.rt, env.matches);
    debug!("Tags: {:?}", tags);
    env.bk.iter_files(module, &parser).and_then(|files| {
        let f = files.filter(|file| {
            if tags.len() != 0 {
                debug!("Checking tags of: {:?}", file.id());
                get_tags_from_header(&file.header()).iter()
                    .any(|t| tags.contains(t))
            } else {
                true
            }
        }).filter(|file| {
            debug!("Checking matches of: {:?}", file.id());
            get_matcher(env.rt, env.matches)
                .and_then(|r| Some(file.matches_with(&r)))
                .unwrap_or(true)
        }).collect::<Vec<File>>();
        Some(f)
    }).unwrap_or(Vec::<File>::new()).into_iter()
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<commit_msg>Implement remove_command()<commit_after>use runtime::Runtime;
use storage::backend::{StorageBackendError, StorageBackend};

use module::Module;
use module::ModuleError;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use module::bm::header::get_tags_from_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;
use ui::file::{FilePrinter, TablePrinter};
use std::vec::IntoIter;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    let url = env.matches.value_of("url").unwrap();
    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    putres.map_err(|sberr| {
        let mut err = ModuleError::new("Storage Backend Error");
        err.caused_by = Some(Box::new(sberr));
        err
    })
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let printer = TablePrinter::new(env.rt.is_verbose(), env.rt.is_debugging());
    let files   = get_filtered_files_from_backend(module, &env);

    debug!("Printing files now");
    printer.print_files(files);

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    if let Some(id) = get_id(env.rt, env.matches) {
        debug!("Remove by id: {}", id);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let file   = env.bk.get_file_by_id(module, &id, &parser).unwrap();
        debug!("Remove file  : {:?}", file);

        if let Err(e) = env.bk.remove_file(module, file) {
            debug!("Remove failed");
            let mut err = ModuleError::new("Removing file failed");
            err.caused_by = Some(Box::new(e));
            Err(err)
        } else {
            debug!("Remove worked");
            Ok(())
        }
    } else {
        debug!("Remove more than one file");

        let files = get_filtered_files_from_backend(module, &env);
        let nfiles = files.len();
        info!("Removing {} Files", nfiles);

        let errs = files.map(|file| {
                debug!("Remove file: {:?}", file);
                env.bk.remove_file(module, file)
            })
            .filter(|e| e.is_err())
            .map(|e| {
                let err = e.err().unwrap();
                warn!("Error occured in Filesystem operation: {}", err);
                err
            })
            .collect::<Vec<StorageBackendError>>();

        let nerrs = errs.len();

        if nerrs != 0 {
            warn!("{} Errors occured while removing {} files", nerrs, nfiles);
            let moderr = ModuleError::new("File removal failed");

            // TODO : Collect StorageBackendErrors

            Err(moderr)
        } else {
            Ok(())
        }
    }
}

/*
 *
 * Private helpers
 *
 */

fn get_filtered_files_from_backend<'a>(module: &'a Module,
                                       env: &CommandEnv) -> IntoIter<File<'a>>
{
    let parser = Parser::new(JsonHeaderParser::new(None));
    let tags = get_tags(env.rt, env.matches);
    debug!("Tags: {:?}", tags);
    env.bk.iter_files(module, &parser).and_then(|files| {
        let f = files.filter(|file| {
            if tags.len() != 0 {
                debug!("Checking tags of: {:?}", file.id());
                get_tags_from_header(&file.header()).iter()
                    .any(|t| tags.contains(t))
            } else {
                true
            }
        }).filter(|file| {
            debug!("Checking matches of: {:?}", file.id());
            get_matcher(env.rt, env.matches)
                .and_then(|r| Some(file.matches_with(&r)))
                .unwrap_or(true)
        }).collect::<Vec<File>>();
        Some(f)
    }).unwrap_or(Vec::<File>::new()).into_iter()
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<|endoftext|>


<commit_before>/*
 * This example imitates the behavior of a friend of mine that often times out on irc.
 * The server often lets you know that he hasn't received any message from the past 5
 * minutes (time out). This program simulates the behavior by not replying to ping
 * messages sent by the server. The server ends up closing the connection since it
 * believes it's dead. The program will reconnect to the server right after disconnecting.
 * The behavior is triggered when you speak to the bot using its nickname.
 */
#![allow(unused_variables)]
#![allow(unused_must_use)]

extern crate hiirc;

use hiirc::*;

struct Fifteen {
    reply_to_ping: bool,
}

static NICKNAME: &'static str = "fifteenistimeout";
static USERNAME: &'static str = "hiirc";
static REALNAME: &'static str = "Fifteen should stop timing out";

impl Listener for Fifteen {

    fn any(&mut self, irc: &Irc, event: &Event) {
        println!("{:?}", event);
    }

    fn channel_msg(&mut self, irc: &Irc, channel: &Channel, user: &ChannelUser, msg: &str) {
        if msg.starts_with(NICKNAME) {
            self.reply_to_ping = false;
        }
    }

    fn ping(&mut self, irc: &Irc, server: &str) {
        if self.reply_to_ping {
            irc.pong(server);
        }
    }

    fn reconnect(&mut self, irc: &Irc) {
        self.reply_to_ping = true;
    }

    fn welcome(&mut self, irc: &Irc) {
        irc.join("#SexManiac", None);
    }

}

fn main() {
    let mut settings = Settings::new("irc.freenode.net:6667", NICKNAME);
    settings.username = USERNAME;
    settings.realname = REALNAME;
    settings.reconnection = ReconnectionSettings::Reconnect {
        max_attempts: 0,
        delay_between_attempts: Duration::seconds(5),
        delay_after_disconnect: Duration::seconds(15),
    };
    settings.auto_ping = false;

    dispatch(Fifteen{ reply_to_ping: true }, settings).unwrap();
}
<commit_msg>examples/fifteen: Fix FifteenIsTimeout capitalization<commit_after>/*
 * This example imitates the behavior of a friend of mine that often times out on irc.
 * The server often lets you know that he hasn't received any message from the past 5
 * minutes (time out). This program simulates the behavior by not replying to ping
 * messages sent by the server. The server ends up closing the connection since it
 * believes it's dead. The program will reconnect to the server right after disconnecting.
 * The behavior is triggered when you speak to the bot using its nickname.
 */
#![allow(unused_variables)]
#![allow(unused_must_use)]

extern crate hiirc;

use hiirc::*;

struct Fifteen {
    reply_to_ping: bool,
}

static NICKNAME: &'static str = "FifteenIsTimeout";
static USERNAME: &'static str = "hiirc";
static REALNAME: &'static str = "Fifteen should stop timing out";

impl Listener for Fifteen {

    fn any(&mut self, irc: &Irc, event: &Event) {
        println!("{:?}", event);
    }

    fn channel_msg(&mut self, irc: &Irc, channel: &Channel, user: &ChannelUser, msg: &str) {
        if msg.starts_with(NICKNAME) {
            self.reply_to_ping = false;
        }
    }

    fn ping(&mut self, irc: &Irc, server: &str) {
        if self.reply_to_ping {
            irc.pong(server);
        }
    }

    fn reconnect(&mut self, irc: &Irc) {
        self.reply_to_ping = true;
    }

    fn welcome(&mut self, irc: &Irc) {
        irc.join("#SexManiac", None);
    }

}

fn main() {
    let mut settings = Settings::new("irc.freenode.net:6667", NICKNAME);
    settings.username = USERNAME;
    settings.realname = REALNAME;
    settings.reconnection = ReconnectionSettings::Reconnect {
        max_attempts: 0,
        delay_between_attempts: Duration::seconds(5),
        delay_after_disconnect: Duration::seconds(15),
    };
    settings.auto_ping = false;

    dispatch(Fifteen{ reply_to_ping: true }, settings).unwrap();
}
<|endoftext|>


<commit_before>extern crate rl_sys;
extern crate nix;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::rc::Rc;

use rl_sys::readline;
use nix::libc;

mod directive;
use directive::{
  Directive,
  Help
};

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Continue(String),
  Quit
}

pub struct Repl {
  pub sout: Rc<RefCell<io::Write>>, // stream out,
  pub serr: Rc<RefCell<io::Write>>, // stream err,
}

impl Repl {
  pub fn new(sout: Rc<RefCell<io::Write>>, serr: Rc<RefCell<io::Write>>) -> Repl {
    Repl {
      sout: sout,
      serr: serr,
    }
  }

  pub fn write_out(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_out(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn write_err(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_err(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn run(&self) {
    self.write_out(b"Welcome to Flang version 0.1. (Type :help for assistance.)\n");
    self.flush_out();

    let mut state = ReplState::new();

    let mut prompt: String = "\x1b[33mflang> \x1b[0m".to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done => {}
            ReplAction::Continue(p) => prompt = p,
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.write_err(format!("ERROR: {}", msg).as_bytes());
        }
      }
    }
  }

  fn handle_line(&self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      InputType::Directive(d, args) => self.exec_directive(d, args),
      InputType::ExecuteSource(line) => self.exec_line(line),
      InputType::ExternalComamnd(command) => self.exec_external_program(command),
      InputType::Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => { // directive
          InputType::Directive(&first[1..], tokens.collect::<Vec<_>>())
        }
        x if x.starts_with('!') => { // external command
          InputType::ExternalComamnd(&line[1..])
        }
        _ => {
          InputType::ExecuteSource(line)
        }
      },
      None => InputType::Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_line(&self, line: &str) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

pub struct ReplState;

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

pub struct Input;

pub struct Output;

pub struct CompilerInstance;

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
  }
}

fn exec_line(src_file: &mut SourceFile, line: &str) {
  // append source
  // generate mir
  // check ast error
  // add source which is valid
  // skip if the source doesn't need to run
  // generate llvm ir and module
  // reorganize llvm module
}

<commit_msg>Simplify InputType in lib/repl<commit_after>extern crate rl_sys;
extern crate nix;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::rc::Rc;
use rl_sys::readline;

use nix::libc;

mod directive;
use InputType::*;

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Continue(String),
  Quit
}

pub struct Repl {
  pub sout: Rc<RefCell<io::Write>>, // stream out,
  pub serr: Rc<RefCell<io::Write>>, // stream err,
}

impl Repl {
  pub fn new(sout: Rc<RefCell<io::Write>>, serr: Rc<RefCell<io::Write>>) -> Repl {
    Repl {
      sout: sout,
      serr: serr,
    }
  }

  pub fn write_out(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_out(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn write_err(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_err(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn run(&self) {
    self.write_out(b"Welcome to Flang version 0.1. (Type :help for assistance.)\n");
    self.flush_out();

    let mut state = ReplState::new();

    let mut prompt: String = "\x1b[33mflang> \x1b[0m".to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done => {}
            ReplAction::Continue(p) => prompt = p,
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.write_err(format!("ERROR: {}", msg).as_bytes());
        }
      }
    }
  }

  fn handle_line(&self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      Directive(d, args) => self.exec_directive(d, args),
      ExecuteSource(line) => self.exec_line(line),
      ExternalComamnd(command) => self.exec_external_program(command),
      Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => Directive(&first[1..], tokens.collect::<Vec<_>>()),
        x if x.starts_with('!') => ExternalComamnd(&line[1..]),
        _ => ExecuteSource(line)
      },
      None => Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_line(&self, line: &str) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

pub struct ReplState;

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

pub struct Input;

pub struct Output;

pub struct CompilerInstance;

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
  }
}

fn exec_line(src_file: &mut SourceFile, line: &str) {
  // append source
  // generate mir
  // check ast error
  // add source which is valid
  // skip if the source doesn't need to run
  // generate llvm ir and module
  // reorganize llvm module
}

<|endoftext|>


<commit_before>//! Online SQLite backup API.
//!
//! To create a `Backup`, you must have two distinct `SqliteConnection`s - one
//! for the source (which can be used while the backup is running) and one for
//! the destination (which cannot).  A `Backup` handle exposes three methods:
//! `step` will attempt to back up a specified number of pages, `progress` gets
//! the current progress of the backup as of the last call to `step`, and
//! `run_to_completion` will attempt to back up the entire source database,
//! allowing you to specify how many pages are backed up at a time and how long
//! the thread should sleep between chunks of pages.
//!
//! The following example is equivalent to "Example 2: Online Backup of a
//! Running Database" from [SQLite's Online Backup API
//! documentation](https://www.sqlite.org/backup.html).
//!
//! ```rust,no_run
//! # use rusqlite::{backup, SqliteConnection, SqliteResult};
//! # use std::path::Path;
//! # use std::time;
//!
//! fn backupDb<P: AsRef<Path>>(src: &SqliteConnection, dst: P, progress: fn(backup::Progress))
//!     -> SqliteResult<()> {
//!     let mut dst = try!(SqliteConnection::open(dst));
//!     let backup = try!(backup::Backup::new(src, &mut dst));
//!     backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress))
//! }
//! ```

use std::marker::PhantomData;

use libc::c_int;
use std::thread;
use std::time::Duration;

use ffi;

use {DatabaseName, SqliteConnection, SqliteError, SqliteResult};

/// Possible successful results of calling `Backup::step`.
pub enum StepResult {
    /// The backup is complete.
    Done,

    /// The step was successful but there are still more pages that need to be backed up.
    More,

    /// The step failed because appropriate locks could not be aquired. This is
    /// not a fatal error - the step can be retried.
    Busy,

    /// The step failed because the source connection was writing to the
    /// database. This is not a fatal error - the step can be retried.
    Locked,
}

/// Struct specifying the progress of a backup. The percentage completion can
/// be calculated as `(pagecount - remaining) / pagecount`. The progress of a
/// backup is as of the last call to `step` - if the source database is
/// modified after a call to `step`, the progress value will become outdated
/// and potentially incorrect.
#[derive(Copy,Clone,Debug)]
pub struct Progress {
    /// Number of pages in the source database that still need to be backed up.
    pub remaining: c_int,
    /// Total number of pages in the source database.
    pub pagecount: c_int,
}

/// A handle to an online backup.
pub struct Backup<'a, 'b> {
    phantom_from: PhantomData<&'a ()>,
    phantom_to: PhantomData<&'b ()>,
    b: *mut ffi::sqlite3_backup,
}

impl<'a, 'b> Backup<'a, 'b> {
    /// Attempt to create a new handle that will allow backups from `from` to
    /// `to`. Note that `to` is a `&mut` - this is because SQLite forbids any
    /// API calls on the destination of a backup while the backup is taking
    /// place.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_init` call returns
    /// `NULL`.
    pub fn new(from: &'a SqliteConnection,
               to: &'b mut SqliteConnection)
               -> SqliteResult<Backup<'a, 'b>> {
        Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main)
    }

    /// Attempt to create a new handle that will allow backups from the
    /// `from_name` database of `from` to the `to_name` database of `to`. Note
    /// that `to` is a `&mut` - this is because SQLite forbids any API calls on
    /// the destination of a backup while the backup is taking place.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_init` call returns
    /// `NULL`.
    pub fn new_with_names(from: &'a SqliteConnection,
                          from_name: DatabaseName,
                          to: &'b mut SqliteConnection,
                          to_name: DatabaseName)
                          -> SqliteResult<Backup<'a, 'b>> {
        let to_name = try!(to_name.to_cstring());
        let from_name = try!(from_name.to_cstring());

        let to_db = to.db.borrow_mut().db;

        let b = unsafe {
            let b = ffi::sqlite3_backup_init(to_db,
                                             to_name.as_ptr(),
                                             from.db.borrow_mut().db,
                                             from_name.as_ptr());
            if b.is_null() {
                return Err(SqliteError::from_handle(to_db, ffi::sqlite3_errcode(to_db)));
            }
            b
        };

        Ok(Backup {
            phantom_from: PhantomData,
            phantom_to: PhantomData,
            b: b,
        })
    }

    /// Gets the progress of the backup as of the last call to `step`.
    pub fn progress(&self) -> Progress {
        unsafe {
            Progress {
                remaining: ffi::sqlite3_backup_remaining(self.b),
                pagecount: ffi::sqlite3_backup_pagecount(self.b),
            }
        }
    }

    /// Attempts to back up the given number of pages. If `num_pages` is
    /// negative, will attempt to back up all remaining pages. This will hold a
    /// lock on the source database for the duration, so it is probably not
    /// what you want for databases that are currently active (see
    /// `run_to_completion` for a better alternative).
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_step` call returns
    /// an error code other than `DONE`, `OK`, `BUSY`, or `LOCKED`. `BUSY` and
    /// `LOCKED` are transient errors and are therefore returned as possible
    /// `Ok` values.
    pub fn step(&self, num_pages: c_int) -> SqliteResult<StepResult> {
        use self::StepResult::{Done, More, Busy, Locked};

        let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) };
        match rc {
            ffi::SQLITE_DONE => Ok(Done),
            ffi::SQLITE_OK => Ok(More),
            ffi::SQLITE_BUSY => Ok(Busy),
            ffi::SQLITE_LOCKED => Ok(Locked),
            rc => {
                Err(SqliteError {
                    code: rc,
                    message: ffi::code_to_str(rc).into(),
                })
            }
        }
    }

    /// Attempts to run the entire backup. Will call `step(pages_per_step)` as
    /// many times as necessary, sleeping for `pause_between_pages` between
    /// each call to give the source database time to process any pending
    /// queries. This is a direct implementation of "Example 2: Online Backup
    /// of a Running Database" from [SQLite's Online Backup API
    /// documentation](https://www.sqlite.org/backup.html).
    ///
    /// If `progress` is not `None`, it will be called after each step with the
    /// current progress of the backup. Note that is possible the progress may
    /// not change if the step returns `Busy` or `Locked` even though the
    /// backup is still running.
    ///
    /// # Failure
    ///
    /// Will return `Err` if any of the calls to `step` return `Err`.
    pub fn run_to_completion(&self,
                             pages_per_step: c_int,
                             pause_between_pages: Duration,
                             progress: Option<fn(Progress)>)
                             -> SqliteResult<()> {
        use self::StepResult::{Done, More, Busy, Locked};

        assert!(pages_per_step > 0, "pages_per_step must be positive");

        loop {
            let r = try!(self.step(pages_per_step));
            if let Some(progress) = progress {
                progress(self.progress())
            }
            match r {
                More | Busy | Locked => thread::sleep(pause_between_pages),
                Done => return Ok(()),
            }
        }
    }
}

impl<'a, 'b> Drop for Backup<'a, 'b> {
    fn drop(&mut self) {
        unsafe { ffi::sqlite3_backup_finish(self.b) };
    }
}

#[cfg(test)]
mod test {
    use {SqliteConnection, DatabaseName};
    use std::time::Duration;
    use super::Backup;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "BEGIN;
                   CREATE TABLE foo(x INTEGER);
                   INSERT INTO foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new(&src, &mut dst).unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new(&src, &mut dst).unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup_temp() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "BEGIN;
                   CREATE TEMPORARY TABLE foo(x INTEGER);
                   INSERT INTO foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Temp,
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Temp,
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup_attached() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "ATTACH DATABASE ':memory:' AS my_attached;
                   BEGIN;
                   CREATE TABLE my_attached.foo(x INTEGER);
                   INSERT INTO my_attached.foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Attached("my_attached"),
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Attached("my_attached"),
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }
}
<commit_msg>Add backup and restore methods to SqliteConnection (behind `backup` Cargo feature)<commit_after>//! Online SQLite backup API.
//!
//! To create a `Backup`, you must have two distinct `SqliteConnection`s - one
//! for the source (which can be used while the backup is running) and one for
//! the destination (which cannot).  A `Backup` handle exposes three methods:
//! `step` will attempt to back up a specified number of pages, `progress` gets
//! the current progress of the backup as of the last call to `step`, and
//! `run_to_completion` will attempt to back up the entire source database,
//! allowing you to specify how many pages are backed up at a time and how long
//! the thread should sleep between chunks of pages.
//!
//! The following example is equivalent to "Example 2: Online Backup of a
//! Running Database" from [SQLite's Online Backup API
//! documentation](https://www.sqlite.org/backup.html).
//!
//! ```rust,no_run
//! # use rusqlite::{backup, SqliteConnection, SqliteResult};
//! # use std::path::Path;
//! # use std::time;
//!
//! fn backupDb<P: AsRef<Path>>(src: &SqliteConnection, dst: P, progress: fn(backup::Progress))
//!     -> SqliteResult<()> {
//!     let mut dst = try!(SqliteConnection::open(dst));
//!     let backup = try!(backup::Backup::new(src, &mut dst));
//!     backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress))
//! }
//! ```

use std::marker::PhantomData;
use std::path::Path;
use std::ptr;

use libc::c_int;
use std::thread;
use std::time::Duration;

use ffi;

use {DatabaseName, SqliteConnection, SqliteError, SqliteResult};

impl SqliteConnection {
    /// Back up the `name` database to the given destination path.
    /// If `progress` is not `None`, it will be called periodically
    /// until the backup completes.
    ///
    /// For more fine-grained control over the backup process (e.g.,
    /// to sleep periodically during the backup or to back up to an
    /// already-open database connection), see the `backup` module.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the destination path cannot be opened
    /// or if the backup fails.
    pub fn backup<P: AsRef<Path>>(&self,
                                  name: DatabaseName,
                                  dst_path: P,
                                  progress: Option<fn(Progress)>)
                                  -> SqliteResult<()> {
        use self::StepResult::{More, Done, Busy, Locked};
        let mut dst = try!(SqliteConnection::open(dst_path));
        let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main));

        let mut r = More;
        while r == More {
            r = try!(backup.step(100));
            if let Some(f) = progress {
                f(backup.progress());
            }
        }

        match r {
            Done => Ok(()),
            Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)),
            Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)),
            More => unreachable!(),
        }
    }

    /// Restore the given source path into the `name` database.
    /// If `progress` is not `None`, it will be called periodically
    /// until the restore completes.
    ///
    /// For more fine-grained control over the restore process (e.g.,
    /// to sleep periodically during the restore or to restore from an
    /// already-open database connection), see the `backup` module.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the destination path cannot be opened
    /// or if the restore fails.
    pub fn restore<P: AsRef<Path>>(&mut self,
                                   name: DatabaseName,
                                   src_path: P,
                                   progress: Option<fn(Progress)>)
                                   -> SqliteResult<()> {
        use self::StepResult::{More, Done, Busy, Locked};
        let src = try!(SqliteConnection::open(src_path));
        let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name));

        let mut r = More;
        let mut busy_count = 0i32;
        'restore_loop: while r == More || r == Busy {
            r = try!(restore.step(100));
            if let Some(f) = progress {
                f(restore.progress());
            }
            if r == Busy {
                busy_count += 1;
                if busy_count >= 3 {
                    break 'restore_loop;
                }
                thread::sleep(Duration::from_millis(100));
            }
        }

        match r {
            Done => Ok(()),
            Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)),
            Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)),
            More => unreachable!(),
        }
    }
}

/// Possible successful results of calling `Backup::step`.
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
pub enum StepResult {
    /// The backup is complete.
    Done,

    /// The step was successful but there are still more pages that need to be backed up.
    More,

    /// The step failed because appropriate locks could not be aquired. This is
    /// not a fatal error - the step can be retried.
    Busy,

    /// The step failed because the source connection was writing to the
    /// database. This is not a fatal error - the step can be retried.
    Locked,
}

/// Struct specifying the progress of a backup. The percentage completion can
/// be calculated as `(pagecount - remaining) / pagecount`. The progress of a
/// backup is as of the last call to `step` - if the source database is
/// modified after a call to `step`, the progress value will become outdated
/// and potentially incorrect.
#[derive(Copy,Clone,Debug)]
pub struct Progress {
    /// Number of pages in the source database that still need to be backed up.
    pub remaining: c_int,
    /// Total number of pages in the source database.
    pub pagecount: c_int,
}

/// A handle to an online backup.
pub struct Backup<'a, 'b> {
    phantom_from: PhantomData<&'a ()>,
    phantom_to: PhantomData<&'b ()>,
    b: *mut ffi::sqlite3_backup,
}

impl<'a, 'b> Backup<'a, 'b> {
    /// Attempt to create a new handle that will allow backups from `from` to
    /// `to`. Note that `to` is a `&mut` - this is because SQLite forbids any
    /// API calls on the destination of a backup while the backup is taking
    /// place.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_init` call returns
    /// `NULL`.
    pub fn new(from: &'a SqliteConnection,
               to: &'b mut SqliteConnection)
               -> SqliteResult<Backup<'a, 'b>> {
        Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main)
    }

    /// Attempt to create a new handle that will allow backups from the
    /// `from_name` database of `from` to the `to_name` database of `to`. Note
    /// that `to` is a `&mut` - this is because SQLite forbids any API calls on
    /// the destination of a backup while the backup is taking place.
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_init` call returns
    /// `NULL`.
    pub fn new_with_names(from: &'a SqliteConnection,
                          from_name: DatabaseName,
                          to: &'b mut SqliteConnection,
                          to_name: DatabaseName)
                          -> SqliteResult<Backup<'a, 'b>> {
        let to_name = try!(to_name.to_cstring());
        let from_name = try!(from_name.to_cstring());

        let to_db = to.db.borrow_mut().db;

        let b = unsafe {
            let b = ffi::sqlite3_backup_init(to_db,
                                             to_name.as_ptr(),
                                             from.db.borrow_mut().db,
                                             from_name.as_ptr());
            if b.is_null() {
                return Err(SqliteError::from_handle(to_db, ffi::sqlite3_errcode(to_db)));
            }
            b
        };

        Ok(Backup {
            phantom_from: PhantomData,
            phantom_to: PhantomData,
            b: b,
        })
    }

    /// Gets the progress of the backup as of the last call to `step`.
    pub fn progress(&self) -> Progress {
        unsafe {
            Progress {
                remaining: ffi::sqlite3_backup_remaining(self.b),
                pagecount: ffi::sqlite3_backup_pagecount(self.b),
            }
        }
    }

    /// Attempts to back up the given number of pages. If `num_pages` is
    /// negative, will attempt to back up all remaining pages. This will hold a
    /// lock on the source database for the duration, so it is probably not
    /// what you want for databases that are currently active (see
    /// `run_to_completion` for a better alternative).
    ///
    /// # Failure
    ///
    /// Will return `Err` if the underlying `sqlite3_backup_step` call returns
    /// an error code other than `DONE`, `OK`, `BUSY`, or `LOCKED`. `BUSY` and
    /// `LOCKED` are transient errors and are therefore returned as possible
    /// `Ok` values.
    pub fn step(&self, num_pages: c_int) -> SqliteResult<StepResult> {
        use self::StepResult::{Done, More, Busy, Locked};

        let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) };
        match rc {
            ffi::SQLITE_DONE => Ok(Done),
            ffi::SQLITE_OK => Ok(More),
            ffi::SQLITE_BUSY => Ok(Busy),
            ffi::SQLITE_LOCKED => Ok(Locked),
            rc => {
                Err(SqliteError {
                    code: rc,
                    message: ffi::code_to_str(rc).into(),
                })
            }
        }
    }

    /// Attempts to run the entire backup. Will call `step(pages_per_step)` as
    /// many times as necessary, sleeping for `pause_between_pages` between
    /// each call to give the source database time to process any pending
    /// queries. This is a direct implementation of "Example 2: Online Backup
    /// of a Running Database" from [SQLite's Online Backup API
    /// documentation](https://www.sqlite.org/backup.html).
    ///
    /// If `progress` is not `None`, it will be called after each step with the
    /// current progress of the backup. Note that is possible the progress may
    /// not change if the step returns `Busy` or `Locked` even though the
    /// backup is still running.
    ///
    /// # Failure
    ///
    /// Will return `Err` if any of the calls to `step` return `Err`.
    pub fn run_to_completion(&self,
                             pages_per_step: c_int,
                             pause_between_pages: Duration,
                             progress: Option<fn(Progress)>)
                             -> SqliteResult<()> {
        use self::StepResult::{Done, More, Busy, Locked};

        assert!(pages_per_step > 0, "pages_per_step must be positive");

        loop {
            let r = try!(self.step(pages_per_step));
            if let Some(progress) = progress {
                progress(self.progress())
            }
            match r {
                More | Busy | Locked => thread::sleep(pause_between_pages),
                Done => return Ok(()),
            }
        }
    }
}

impl<'a, 'b> Drop for Backup<'a, 'b> {
    fn drop(&mut self) {
        unsafe { ffi::sqlite3_backup_finish(self.b) };
    }
}

#[cfg(test)]
mod test {
    use {SqliteConnection, DatabaseName};
    use std::time::Duration;
    use super::Backup;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "BEGIN;
                   CREATE TABLE foo(x INTEGER);
                   INSERT INTO foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new(&src, &mut dst).unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new(&src, &mut dst).unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup_temp() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "BEGIN;
                   CREATE TEMPORARY TABLE foo(x INTEGER);
                   INSERT INTO foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Temp,
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Temp,
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_backup_attached() {
        let src = SqliteConnection::open_in_memory().unwrap();
        let sql = "ATTACH DATABASE ':memory:' AS my_attached;
                   BEGIN;
                   CREATE TABLE my_attached.foo(x INTEGER);
                   INSERT INTO my_attached.foo VALUES(42);
                   END;";
        src.execute_batch(sql).unwrap();

        let mut dst = SqliteConnection::open_in_memory().unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Attached("my_attached"),
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.step(-1).unwrap();
        }

        let the_answer = dst.query_row("SELECT x FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42, the_answer);

        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();

        {
            let backup = Backup::new_with_names(&src,
                                                DatabaseName::Attached("my_attached"),
                                                &mut dst,
                                                DatabaseName::Main)
                             .unwrap();
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
        }

        let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::<i64>(0)).unwrap();
        assert_eq!(42 + 43, the_answer);
    }
}
<|endoftext|>


<commit_before>use std::str;
use std::mem::swap;

use error::Error;

pub struct Buffer {
    front_buf: String,
    back_buf: String,
    pos: usize
}

impl Buffer {

    pub fn new() -> Buffer {
        Buffer {
            front_buf: String::new(),
            back_buf: String::new(),
            pos: 0
        }
    }

    pub fn swap(&mut self) {
        swap(&mut self.front_buf, &mut self.back_buf);
        self.pos = self.front_buf.len();
    }

    pub fn reset(&mut self) {
        self.front_buf.clear();
        self.pos = 0;
    }

    pub fn replace(&mut self, s: &str) {
        self.front_buf.clear();
        self.front_buf.push_str(s);
        self.pos = s.len();
    }

    pub fn insert_string_at_cursor(&mut self, s: &str) {
        self.front_buf.push_str(s);
        self.pos += s.len();
    }

    pub fn insert_bytes_at_cursor(&mut self, s: &[u8]) -> Result<(), Error> {
        let c = try!(str::from_utf8(s).map_err(|_| Error::InvalidUTF8));
        self.insert_string_at_cursor(c);
        Ok(())
    }

    pub fn delete_char_left_of_cursor(&mut self) {
        if self.pos > 0 {
            self.front_buf.remove(self.pos-1);
            self.pos -= 1;
        }
    }

    pub fn move_left(&mut self) {
        if self.pos > 0 {
            self.pos -= 1;
        }
    }

    pub fn move_right(&mut self) {
        if self.pos < self.front_buf.len() {
            self.pos += 1;
        }
    }

    pub fn move_start(&mut self) {
        self.pos = 0;
    }

    pub fn move_end(&mut self) {
        self.pos = self.front_buf.len();
    }

    pub fn get_line(&self, prompt: &str) -> Vec<u8> {
        let mut seq = String::new();
        seq.push('\r');
        seq.push_str(prompt);
        seq.push_str(&self.front_buf);
        seq.push_str("\x1b[0K");
        seq.push_str(&format!("\r\x1b[{}C", prompt.len() + self.pos));
        seq.into_bytes()
    }

    pub fn to_string(self) -> String {
        self.front_buf
    }

}
<commit_msg>fix string insertion into buffer<commit_after>use std::str;
use std::mem::swap;

use error::Error;

pub struct Buffer {
    front_buf: String,
    back_buf: String,
    pos: usize
}

impl Buffer {

    pub fn new() -> Buffer {
        Buffer {
            front_buf: String::new(),
            back_buf: String::new(),
            pos: 0
        }
    }

    pub fn swap(&mut self) {
        swap(&mut self.front_buf, &mut self.back_buf);
        self.pos = self.front_buf.len();
    }

    pub fn reset(&mut self) {
        self.front_buf.clear();
        self.pos = 0;
    }

    pub fn replace(&mut self, s: &str) {
        self.front_buf.clear();
        self.front_buf.push_str(s);
        self.pos = s.len();
    }

    pub fn insert_char_at_cursor(&mut self, c: char) {
        let len = c.len_utf8();
        self.front_buf.insert(self.pos, c);
        self.pos += len;
    }

    pub fn insert_string_at_cursor(&mut self, s: &str) {
        for c in s.chars() {
            self.insert_char_at_cursor(c);
        }
    }

    pub fn insert_bytes_at_cursor(&mut self, s: &[u8]) -> Result<(), Error> {
        let c = try!(str::from_utf8(s).map_err(|_| Error::InvalidUTF8));
        self.insert_string_at_cursor(c);
        Ok(())
    }

    pub fn delete_char_left_of_cursor(&mut self) {
        if self.pos > 0 {
            self.front_buf.remove(self.pos-1);
            self.pos -= 1;
        }
    }

    pub fn move_left(&mut self) {
        if self.pos > 0 {
            self.pos -= 1;
        }
    }

    pub fn move_right(&mut self) {
        if self.pos < self.front_buf.len() {
            self.pos += 1;
        }
    }

    pub fn move_start(&mut self) {
        self.pos = 0;
    }

    pub fn move_end(&mut self) {
        self.pos = self.front_buf.len();
    }

    pub fn get_line(&self, prompt: &str) -> Vec<u8> {
        let mut seq = String::new();
        seq.push('\r');
        seq.push_str(prompt);
        seq.push_str(&self.front_buf);
        seq.push_str("\x1b[0K");
        seq.push_str(&format!("\r\x1b[{}C", prompt.len() + self.pos));
        seq.into_bytes()
    }

    pub fn to_string(self) -> String {
        self.front_buf
    }

}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::io::Read;
use std::collections::HashMap;
use mode::Command;
use std::cmp;
use std::cmp::Ordering;
use std;

use geometry;
use errors::CrbError;

/// A reference to a position.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Anchor {
    id: i64,
}

#[derive(PartialEq, Eq)]
pub struct Wrap {
    style: WrapStyle,
    width: i32,
    /// Whether the cursor moves between visual lines or buffer lines.
    vismove: bool,
}

#[derive(PartialEq, Eq)]
#[allow(dead_code)]
pub enum WrapStyle {
    Truncate,
    Hard,
    Word,
}

impl Wrap {
    pub fn default(width: i32) -> Wrap {
        Wrap {
            style: WrapStyle::Truncate,
            width: width,
            vismove: false,
        }
    }
}

/// Private structure containing position data.
#[derive(Clone, PartialOrd)]
struct Position {
    line: i32,
    /// Offset from the beginning of the line.
    /// 0 is before the first character.
    /// len(line) is after the last character.
    offset: i32,
    /// The offset that this will snap back to when moving to a longer line.
    wishful_offset: Option<i32>,
}

impl PartialEq for Position {
    fn eq(&self, other: &Self) -> bool {
        (self.line, self.offset) == (other.line, other.offset)
    }
}

impl Eq for Position {}

impl Ord for Position {
    fn cmp(&self, other: &Self) -> Ordering {
        (self.line, self.offset).cmp(&(other.line, other.offset))
    }
}

struct Line {
    text: String,
}

#[derive(Debug)]
pub struct Display {
    pub x: i32,
    pub y: i32,
    pub symbol: Symbol,
}

#[derive(Debug)]
pub enum Symbol {
    Char(char),
    Anchor(Anchor),
    Void,
}

pub struct Buffer {
    contents: Vec<Line>,
    pub file_path: Option<String>,
    pub unsaved: bool,
    pub newfile: bool,

    // Map from anchor id to position.
    anchors: HashMap<i64, Position>,
    next_anchor_id: i64,
}

impl Buffer {
    /** Creators **/

    pub fn load_from_file(path: &str) -> Result<Buffer, io::Error> {
        let s = try!(read_file(path));
        let contents: Vec<Line> = s.split("\n")
            .map(|x| Line { text: x.to_string() })
            .collect();
        let mut buf = Buffer::empty();
        buf.contents = contents;
        buf.file_path = Some(path.to_string());
        buf.newfile;
        Ok(buf)
    }

    pub fn new_file(path: &str) -> Buffer {
        let mut buf = Buffer::empty();
        buf.file_path = Some(path.to_string());
        buf
    }

    pub fn empty() -> Buffer {
        Buffer {
            contents: Vec::new(),
            file_path: None,
            unsaved: false,
            newfile: true,
            anchors: HashMap::new(),
            next_anchor_id: 0,
        }
    }

    /** Mutators **/

    pub fn new_anchor(&mut self) -> Anchor {
        let a = Anchor { id: self.new_anchor_id() };
        let p = Position {
            line: 0,
            offset: 0,
            wishful_offset: None,
        };
        self.anchors.insert(a.id, p);
        a
    }

    pub fn move_anchor(&mut self, anchor: &Anchor, m: &Command) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut p2: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let m = canonicalize_move(m);
        let p3: Position = match m {
            Command::MoveRight(n) => {
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                p2.offset = cmp::min(cmp::max(0, p2.offset + n), len);
                p2
            }
            Command::MoveDown(n) => {
                p2.line = cmp::min(cmp::max(0, p2.line + n), self.count_lines());
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                let wish = match p2.wishful_offset {
                    None => p2.offset,
                    Some(x) => x,
                };
                p2.offset = cmp::min(wish, len);
                p2.wishful_offset = match p2.offset < wish {
                    true => Some(wish),
                    false => None,
                };
                p2
            }
            _ => return Err(CrbError::new("unsupported move command")),
        };
        self.anchors.insert(anchor.id, p3);
        Ok(())
    }

    fn new_anchor_id(&mut self) -> i64 {
        self.next_anchor_id += 1;
        self.next_anchor_id - 1
    }

    pub fn insert_text_before(&mut self, anchor: &Anchor, text: char) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut pos: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let cur_line = self.contents[pos.line as usize].text.clone();
        let (before, after) = cur_line.split_at(pos.offset as usize);
        self.contents[pos.line as usize] =
            Line { text: before.to_string() + &text.to_string() + &after };
        // TODO move anchors on this line
        pos.offset += 1;
        self.anchors.insert(anchor.id, pos);
        Ok(())
    }

    pub fn delete_at(&mut self, anchor: &Anchor) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut pos: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let cur_line = self.contents[pos.line as usize].text.clone();
        let (before, after) = cur_line.split_at(pos.offset as usize);
        let before_argh: String = before.chars().take(before.len() - 1).collect();
        self.contents[pos.line as usize] = Line { text: before_argh.to_string() + after };
        pos.offset -= 1;
        // TODO move anchors on this line
        self.anchors.insert(anchor.id, pos);
        Ok(())
    }

    /** Observers **/

    pub fn line(&self, i: i32) -> Option<&str> {
        self.contents.get(i as usize).map(|l| l.text.as_str())
    }

    pub fn count_lines(&self) -> i32 {
        self.contents.len() as i32
    }

    /// Calls the closure in scan order on the rectangular area.
    pub fn display<F>(&self, start_line: i32, size: geometry::Size, wrap: Wrap, mut f: F)
        where F: FnMut(&Display)
    {
        if wrap != Wrap::default(wrap.width) {
            panic!("TODO unsupported wrap");
        }

        // TODO horizontal scrolling
        // TODO multi-width characters

        let mut buf_y = start_line as usize;
        let mut buf_x = 0 as usize;
        let mut lines = self.contents.iter().skip(buf_y);
        let mut line_chars = to_chars(lines.next()).peekable();
        // TODO enable anchors
        // let mut line_anchors = self.line_anchors(buf_y).iter().peekable();
        let anchors_all = self.all_anchors();
        let mut anchors_iter = anchors_all.iter().peekable();

        for view_y in 0..size.height {
            for view_x in 0..size.width {
                let mut did_anchor = false;
                if let Some(tpl) = anchors_iter.peek() {
                    let tpl: &(&i64, &Position) = tpl;
                    // let (anchor_id, pos): &(&i64, &Position) = tpl;
                    let anchor_id: &i64 = tpl.0;
                    let pos: &Position = tpl.1;
                    if (pos.line as usize == buf_y) && (pos.offset as usize == buf_x) {
                        let a = Anchor { id: anchor_id + 0 };
                        let d = Display {
                            x: view_x,
                            y: view_y,
                            symbol: Symbol::Anchor(a),
                        };
                        f(&d);
                        did_anchor = true;
                    }
                }
                if did_anchor {
                    anchors_iter.next();
                }

                let s = match line_chars.next() {
                    Some(c) => Symbol::Char(c),
                    None => Symbol::Void,
                };
                let d = Display {
                    x: view_x,
                    y: view_y,
                    symbol: s,
                };
                f(&d);
                buf_x += 1;
            }
            if line_chars.peek().is_none() {
                // Next buf line
                buf_y += 1;
                buf_x = 0;
                line_chars = to_chars(lines.next()).peekable();
                // line_anchors = self.line_anchors(buf_y).iter().peekable();
            }
        }
    }

    fn all_anchors(&self) -> Vec<(&i64, &Position)> {
        let mut ans: Vec<(&i64, &Position)> = self.anchors.iter().collect();
        ans.sort_by_key(|x| x.1);
        ans
    }
}

fn to_chars(oli: Option<&Line>) -> std::str::Chars {
    match oli {
        Some(y) => y.text.chars(),
        None => "".chars(),
    }
}

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut f = try!(fs::File::open(path));
    let mut contents = String::new();
    try!(f.read_to_string(&mut contents));
    Ok(contents)
}

fn canonicalize_move(mov: &Command) -> Command {
    match mov.clone() {
        Command::MoveLeft(n) => Command::MoveRight(-n),
        Command::MoveUp(n) => Command::MoveDown(-n),
        m @ _ => m,
    }
}
<commit_msg>get rid of fluft<commit_after>use std::fs;
use std::io;
use std::io::Read;
use std::collections::HashMap;
use mode::Command;
use std::cmp;
use std::cmp::Ordering;
use std;

use geometry;
use errors::CrbError;

/// A reference to a position.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Anchor {
    id: i64,
}

#[derive(PartialEq, Eq)]
pub struct Wrap {
    style: WrapStyle,
    width: i32,
    /// Whether the cursor moves between visual lines or buffer lines.
    vismove: bool,
}

#[derive(PartialEq, Eq)]
#[allow(dead_code)]
pub enum WrapStyle {
    Truncate,
    Hard,
    Word,
}

impl Wrap {
    pub fn default(width: i32) -> Wrap {
        Wrap {
            style: WrapStyle::Truncate,
            width: width,
            vismove: false,
        }
    }
}

/// Private structure containing position data.
#[derive(Clone, PartialOrd)]
struct Position {
    line: i32,
    /// Offset from the beginning of the line.
    /// 0 is before the first character.
    /// len(line) is after the last character.
    offset: i32,
    /// The offset that this will snap back to when moving to a longer line.
    wishful_offset: Option<i32>,
}

impl PartialEq for Position {
    fn eq(&self, other: &Self) -> bool {
        (self.line, self.offset) == (other.line, other.offset)
    }
}

impl Eq for Position {}

impl Ord for Position {
    fn cmp(&self, other: &Self) -> Ordering {
        (self.line, self.offset).cmp(&(other.line, other.offset))
    }
}

struct Line {
    text: String,
}

#[derive(Debug)]
pub struct Display {
    pub x: i32,
    pub y: i32,
    pub symbol: Symbol,
}

#[derive(Debug)]
pub enum Symbol {
    Char(char),
    Anchor(Anchor),
    Void,
}

pub struct Buffer {
    contents: Vec<Line>,
    pub file_path: Option<String>,
    pub unsaved: bool,
    pub newfile: bool,

    // Map from anchor id to position.
    anchors: HashMap<i64, Position>,
    next_anchor_id: i64,
}

impl Buffer {
    /** Creators **/

    pub fn load_from_file(path: &str) -> Result<Buffer, io::Error> {
        let s = try!(read_file(path));
        let contents: Vec<Line> = s.split("\n")
            .map(|x| Line { text: x.to_string() })
            .collect();
        let mut buf = Buffer::empty();
        buf.contents = contents;
        buf.file_path = Some(path.to_string());
        buf.newfile;
        Ok(buf)
    }

    pub fn new_file(path: &str) -> Buffer {
        let mut buf = Buffer::empty();
        buf.file_path = Some(path.to_string());
        buf
    }

    pub fn empty() -> Buffer {
        Buffer {
            contents: Vec::new(),
            file_path: None,
            unsaved: false,
            newfile: true,
            anchors: HashMap::new(),
            next_anchor_id: 0,
        }
    }

    /** Mutators **/

    pub fn new_anchor(&mut self) -> Anchor {
        let a = Anchor { id: self.new_anchor_id() };
        let p = Position {
            line: 0,
            offset: 0,
            wishful_offset: None,
        };
        self.anchors.insert(a.id, p);
        a
    }

    pub fn move_anchor(&mut self, anchor: &Anchor, m: &Command) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut p2: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let m = canonicalize_move(m);
        let p3: Position = match m {
            Command::MoveRight(n) => {
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                p2.offset = cmp::min(cmp::max(0, p2.offset + n), len);
                p2
            }
            Command::MoveDown(n) => {
                p2.line = cmp::min(cmp::max(0, p2.line + n), self.count_lines());
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                let wish = match p2.wishful_offset {
                    None => p2.offset,
                    Some(x) => x,
                };
                p2.offset = cmp::min(wish, len);
                p2.wishful_offset = match p2.offset < wish {
                    true => Some(wish),
                    false => None,
                };
                p2
            }
            _ => return Err(CrbError::new("unsupported move command")),
        };
        self.anchors.insert(anchor.id, p3);
        Ok(())
    }

    fn new_anchor_id(&mut self) -> i64 {
        self.next_anchor_id += 1;
        self.next_anchor_id - 1
    }

    pub fn insert_text_before(&mut self, anchor: &Anchor, text: char) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut pos: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let cur_line = self.contents[pos.line as usize].text.clone();
        let (before, after) = cur_line.split_at(pos.offset as usize);
        self.contents[pos.line as usize] =
            Line { text: before.to_string() + &text.to_string() + &after };
        // TODO move anchors on this line
        pos.offset += 1;
        self.anchors.insert(anchor.id, pos);
        Ok(())
    }

    pub fn delete_at(&mut self, anchor: &Anchor) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut pos: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let cur_line = self.contents[pos.line as usize].text.clone();
        let (before, after) = cur_line.split_at(pos.offset as usize);
        let before_argh: String = before.chars().take(before.len() - 1).collect();
        self.contents[pos.line as usize] = Line { text: before_argh.to_string() + after };
        pos.offset -= 1;
        // TODO move anchors on this line
        self.anchors.insert(anchor.id, pos);
        Ok(())
    }

    /** Observers **/

    pub fn line(&self, i: i32) -> Option<&str> {
        self.contents.get(i as usize).map(|l| l.text.as_str())
    }

    pub fn count_lines(&self) -> i32 {
        self.contents.len() as i32
    }

    /// Calls the closure in scan order on the rectangular area.
    pub fn display<F>(&self, start_line: i32, size: geometry::Size, wrap: Wrap, mut f: F)
        where F: FnMut(&Display)
    {
        if wrap != Wrap::default(wrap.width) {
            panic!("TODO unsupported wrap");
        }

        // TODO horizontal scrolling
        // TODO multi-width characters

        let mut buf_y = start_line as usize;
        let mut buf_x = 0 as usize;
        let mut lines = self.contents.iter().skip(buf_y);
        let mut line_chars = to_chars(lines.next()).peekable();
        let anchors_all = self.all_anchors();
        let mut anchors_iter = anchors_all.iter().peekable();

        for view_y in 0..size.height {
            for view_x in 0..size.width {
                let mut did_anchor = false;
                if let Some(tpl) = anchors_iter.peek() {
                    let tpl: &(&i64, &Position) = tpl;
                    let anchor_id: &i64 = tpl.0;
                    let pos: &Position = tpl.1;
                    if (pos.line as usize == buf_y) && (pos.offset as usize == buf_x) {
                        let a = Anchor { id: anchor_id + 0 };
                        let d = Display {
                            x: view_x,
                            y: view_y,
                            symbol: Symbol::Anchor(a),
                        };
                        f(&d);
                        did_anchor = true;
                    }
                }
                if did_anchor {
                    anchors_iter.next();
                }

                let s = match line_chars.next() {
                    Some(c) => Symbol::Char(c),
                    None => Symbol::Void,
                };
                let d = Display {
                    x: view_x,
                    y: view_y,
                    symbol: s,
                };
                f(&d);
                buf_x += 1;
            }
            if line_chars.peek().is_none() {
                // Next buf line
                buf_y += 1;
                buf_x = 0;
                line_chars = to_chars(lines.next()).peekable();
            }
        }
    }

    fn all_anchors(&self) -> Vec<(&i64, &Position)> {
        let mut ans: Vec<(&i64, &Position)> = self.anchors.iter().collect();
        ans.sort_by_key(|x| x.1);
        ans
    }
}

fn to_chars(oli: Option<&Line>) -> std::str::Chars {
    match oli {
        Some(y) => y.text.chars(),
        None => "".chars(),
    }
}

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut f = try!(fs::File::open(path));
    let mut contents = String::new();
    try!(f.read_to_string(&mut contents));
    Ok(contents)
}

fn canonicalize_move(mov: &Command) -> Command {
    match mov.clone() {
        Command::MoveLeft(n) => Command::MoveRight(-n),
        Command::MoveUp(n) => Command::MoveDown(-n),
        m @ _ => m,
    }
}
<|endoftext|>