<commit_before>extern crate fast_sweeping;
extern crate surfviz;
#[macro_use]
extern crate glium;
extern crate isosurface;
extern crate ndarray;

use ndarray::Array;
use isosurface::marching_tetrahedra;
use glium::glutin;

use surfviz::surface;
use surfviz::camera;

fn main() {
    let res = 10;
    let h = 1. / res as f64;
    let xs = Array::linspace(-0.5, 0.5, res);
    let ys = Array::linspace(-0.5, 0.5, res);
    let zs = Array::linspace(-0.5, 0.5, res);

    let dim = (xs.len(), ys.len(), zs.len());

    let u = {
        let mut u: Array<f64, _> = Array::from_elem(dim, 0.);

        let r = 0.3;
        for ((i, j, k), u) in u.indexed_iter_mut() {
            let (x, y, z) = (xs[i], ys[j], zs[k]);
            *u = x * x + y * y + z * z - r * r;
            *u = u.min((x - 0.25) * (x - 0.25) + y * y + z * z - 0.2 * 0.2);
        }
        u
    };

    let mut d = u.clone();

    let mut v = u.clone();
    for _ in 0..10 {
        fast_sweeping::signed_distance_3d(d.as_slice_mut().unwrap(), v.as_slice().unwrap(), dim, h);
        v.clone_from(&d);
    }

    let level = 0.0;

    use glium::{DisplayBuild, Surface};
    let display = glium::glutin::WindowBuilder::new()
        .with_depth_buffer(24)
        .build_glium()
        .unwrap();

    let (verts, faces, normals) = marching_tetrahedra(u.as_slice().unwrap(), dim, level);
    let surface = surface::Surface::new(&display, &verts, &faces, &normals);

    let (verts, faces, normals) = marching_tetrahedra(d.as_slice().unwrap(), dim, level);
    let signed_distance_surface = {
        let mut s = surface::Surface::new(&display, &verts, &faces, &normals);
        s.set_color([0., 0., 1., 1.]);
        s
    };

    let mut camera = camera::CameraState::new();
    let mut wireframe = false;
    let mut show_normals = false;

    loop {
        camera.update();

        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();
        signed_distance_surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();

        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                glium::glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::F)) => {
                    wireframe = !wireframe
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::N)) => {
                    show_normals = !show_normals
                }
                ev => camera.process_input(&ev),
            }
        }
    }
}
<commit_msg>Test with a plane<commit_after>extern crate fast_sweeping;
extern crate surfviz;
#[macro_use]
extern crate glium;
extern crate isosurface;
extern crate ndarray;

use ndarray::Array;
use isosurface::marching_tetrahedra;
use glium::glutin;

use surfviz::surface;
use surfviz::camera;

fn main() {
    let res = 10;
    let h = 1. / res as f64;
    let xs = Array::linspace(-0.5, 0.5, res);
    let ys = Array::linspace(-0.5, 0.5, res);
    let zs = Array::linspace(-0.5, 0.5, res);

    let dim = (xs.len(), ys.len(), zs.len());

    let u = {
        let mut u: Array<f64, _> = Array::from_elem(dim, 0.);

        let r = 0.3;
        for ((i, j, k), u) in u.indexed_iter_mut() {
            let (x, y, z) = (xs[i], ys[j], zs[k]);
            // *u = x * x + y * y + z * z - r * r;
            // *u = u.min((x - 0.25) * (x - 0.25) + y * y + z * z - 0.2 * 0.2);
            *u  = (x + y - z) / (3f64).sqrt();
        }
        u
    };

    let mut d = u.clone();

    let mut v = u.clone();
    for _ in 0..10 {
        fast_sweeping::signed_distance_3d(d.as_slice_mut().unwrap(), v.as_slice().unwrap(), dim, h);
        v.clone_from(&d);
    }

    let level = 0.2;

    use glium::{DisplayBuild, Surface};
    let display = glium::glutin::WindowBuilder::new()
        .with_depth_buffer(24)
        .build_glium()
        .unwrap();

    let (verts, faces, normals) = marching_tetrahedra(u.as_slice().unwrap(), dim, level);
    let surface = surface::Surface::new(&display, &verts, &faces, &normals);

    let (verts, faces, normals) = marching_tetrahedra(d.as_slice().unwrap(), dim, level);
    let signed_distance_surface = {
        let mut s = surface::Surface::new(&display, &verts, &faces, &normals);
        s.set_color([0., 0., 1., 1.]);
        s
    };

    let mut camera = camera::CameraState::new();
    let mut wireframe = false;
    let mut show_normals = false;

    loop {
        camera.update();

        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();
        signed_distance_surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();

        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                glium::glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::F)) => {
                    wireframe = !wireframe
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::N)) => {
                    show_normals = !show_normals
                }
                ev => camera.process_input(&ev),
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, 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
//
//     http://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,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.3.0";

#[cfg(not(feature = "static-link"))]
mod imp {
    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        if let Ok(lib) = ::pkg_config::Config::new()
            .atleast_version(GRPC_VERSION)
            .statik(true)
            .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::env;
    use std::fs::DirBuilder;
    use std::path::{Path, PathBuf};
    use std::process::Command;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_GIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";
    const CARES_VERSION: &'static str = "cares-1_12_0";

    fn wget(url: &str, out: &str) -> Result<(), String> {
        Command::new("wget")
            .args(&["-q", "-c", "-O", out, url])
            .status()
            .map_err(|err| format!("wget execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("wget exit with {}", status))
                }
            })
    }

    fn tar_xf(file: &str) -> Result<(), String> {
        Command::new("tar")
            .args(&["zxf", file])
            .status()
            .map_err(|err| format!("tar execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("tar exit with {}", status))
                }
            })
    }

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        let cur_dir = env::current_dir().expect("Can't access current working directory");
        let crate_dir = cur_dir.as_path().display();

        let out_dir = env::var("OUT_DIR").expect("Can't access OUT_DIR");
        for sub_dir in &["grpc", "zlib", "ssl", "cares"] {
            DirBuilder::new().recursive(true).create(format!("{}/{}", out_dir, sub_dir)).unwrap();
        }

        if !Path::new(&format!("grpc-{}", GRPC_VERSION)).exists() {
            wget(&format!("https://github.com/grpc/grpc/archive/v{}.tar.gz",
                          GRPC_VERSION),
                 "grpc.tar.gz")
                .and_then(|_| tar_xf("grpc.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("zlib-{}", ZLIB_VERSION)).exists() {
            wget(&format!("https://github.com/madler/zlib/archive/v{}.tar.gz",
                          ZLIB_VERSION),
                 "zlib.tar.gz")
                .and_then(|_| tar_xf("zlib.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("boringssl-{}", BORINGSSL_GIT_HASH)).exists() {
            wget(&format!("https://github.com/google/boringssl/archive/{}.tar.gz",
                          BORINGSSL_GIT_HASH),
                 "boringssl.tar.gz")
                .and_then(|_| tar_xf("boringssl.tar.gz"))
                .unwrap();
        }

        let cares_path = PathBuf::from(format!("grpc-{}/third_party/cares/cares", GRPC_VERSION));
        if !cares_path.with_file_name("README.md").exists() {
            wget(&format!("https://github.com/c-ares/c-ares/archive/{}.tar.gz",
                          CARES_VERSION),
                 "c-ares.tar.gz")
                .and_then(|_| tar_xf("c-ares.tar.gz"))
                .unwrap();
            let _ = Command::new("rm")
                .current_dir(&format!("c-ares-{}/", CARES_VERSION))
                .args(&["acountry.c", "adig.c", "ahost.c"])
                .status();
            Command::new("cp")
                .args(&["-a", &format!("c-ares-{}/", CARES_VERSION), cares_path.to_str().unwrap()])
                .status()
                .map_err(|err| format!("cp execute failed: {}", err))
                .and_then(|status| {
                    if status.success() {
                        Ok(())
                    } else {
                        Err(format!("cp -a exit with {}", status))
                    }
                })
                .unwrap();
        }

        let zlib_dst = ::cmake::Config::new(format!("zlib-{}", ZLIB_VERSION))
            .out_dir(format!("{}/{}", out_dir, "zlib"))
            .build();

        println!("cargo:rustc-link-search=native={}/build",
                 zlib_dst.display());

        let cares_dst = ::cmake::Config::new(format!("grpc-{}/src/c-ares", GRPC_VERSION))
            .out_dir(format!("{}/{}", out_dir, "cares"))
            .build_target("cares")
            .build();

        println!("cargo:rustc-link-search=native={}/build",
                 cares_dst.display());

        let dst = ::cmake::Config::new(format!("grpc-{}", GRPC_VERSION))
            .define("ZLIB_ROOT_DIR",
                    format!("{}/zlib-{}", crate_dir, ZLIB_VERSION))
            .define("BORINGSSL_ROOT_DIR",
                    format!("{}/boringssl-{}", crate_dir, BORINGSSL_GIT_HASH))
            .out_dir(format!("{}/{}", out_dir, "grpc"))
            .build_target("grpc_unsecure")
            .build();

        println!("cargo:rustc-link-search=native={}/build", dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=cares");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc_unsecure");

        cc.include(format!("grpc-{}/include", GRPC_VERSION));
    }
}

fn main() {
    let mut cc = ::gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<commit_msg>build.rs: now static build at OUT_DIR<commit_after>// Copyright 2017 PingCAP, 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
//
//     http://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,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.3.0";

#[cfg(not(feature = "static-link"))]
mod imp {
    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        if let Ok(lib) = ::pkg_config::Config::new()
               .atleast_version(GRPC_VERSION)
               .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find a dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::env;
    use std::fs::DirBuilder;
    use std::process::Command;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_COMMIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";
    const CARES_TAG: &'static str = "cares-1_12_0";

    struct FakeGitSubmodule {
        url: String,
        base_name: String,
        to_dir: String,
    }

    impl FakeGitSubmodule {
        fn new_with_hash(account: &str,
                         repo: &str,
                         commit_hash: &str,
                         to_dir: &str)
                         -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             commit_hash),
                base_name: format!("{}-{}", repo, commit_hash),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_tag(account: &str, repo: &str, tag: &str, to_dir: &str) -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/{}.tar.gz",
                             account,
                             repo,
                             tag),
                base_name: format!("{}-{}", repo, tag),
                to_dir: to_dir.to_owned(),
            }
        }

        fn new_with_semver(account: &str, repo: &str, ver: &str, to_dir: &str) -> FakeGitSubmodule {
            FakeGitSubmodule {
                url: format!("https://github.com/{}/{}/archive/v{}.tar.gz",
                             account,
                             repo,
                             ver),
                base_name: format!("{}-{}", repo, ver),
                to_dir: to_dir.to_owned(),
            }
        }

        fn init(&self) -> Result<(), String> {
            let out_dir = env::var_os("OUT_DIR").unwrap();
            let tgz_file_name = format!("{}.tar.gz", self.base_name);
            try!(Command::new("wget")
                     .args(&["-q", "-c", "-O", &tgz_file_name, &self.url])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute wget: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("wget exit with {}", status))
                               }));

            try!(Command::new("tar")
                     .args(&["zxf", &tgz_file_name])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute tar: {}", err))
                     .and_then(|status| if status.success() {
                                   Ok(())
                               } else {
                                   Err(format!("tar exit with {}", status))
                               }));
            // clean base dir
            try!(Command::new("rm")
                     .args(&["-r", &self.to_dir])
                     .current_dir(&out_dir)
                     .status()
                     .map_err(|err| format!("failed to execute tar: {}", err)));

            Command::new("mv")
                .args(&[&self.base_name, &self.to_dir])
                .current_dir(&out_dir)
                .status()
                .map_err(|err| format!("mv execute failed: {}", err))
                .and_then(|status| if status.success() {
                              Ok(())
                          } else {
                              Err(format!("mv exit with {}", status))
                          })
        }
    }

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        let out_dir = env::var("OUT_DIR").expect("Can't access OUT_DIR");
        DirBuilder::new()
            .recursive(true)
            .create(format!("{}/{}", out_dir, "grpc"))
            .unwrap();

        FakeGitSubmodule::new_with_semver("grpc", "grpc", GRPC_VERSION, "grpc")
            .init()
            .unwrap();

        FakeGitSubmodule::new_with_semver("madler", "zlib", ZLIB_VERSION, "grpc/third_party/zlib")
            .init()
            .unwrap();

        FakeGitSubmodule::new_with_hash("google",
                                        "boringssl",
                                        BORINGSSL_COMMIT_HASH,
                                        "grpc/third_party/boringssl")
                .init()
                .unwrap();

        FakeGitSubmodule::new_with_tag("c-ares",
                                       "c-ares",
                                       CARES_TAG,
                                       "grpc/third_party/cares/cares")
                .init()
                .unwrap();

        // fix multiple _main symbols
        let _ = Command::new("rm")
            .current_dir(&format!("{}/grpc/third_party/cares/cares", out_dir))
            .args(&["acountry.c", "adig.c", "ahost.c"])
            .status();

        let dst = ::cmake::Config::new(format!("{}/grpc", out_dir))
            .build_target("grpc")
            .build();

        println!("cargo:rustc-link-search=native={}/build", dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/cares",
                 dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
                 dst.display());
        println!("cargo:rustc-link-search=native={}/build", dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=cares");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc");

        cc.include(format!("{}/grpc/include", out_dir));
    }
}

fn main() {
    let mut cc = ::gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<|endoftext|>


<commit_before>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

use na::{grustna_call_on_stack,grustna_call_off_stack};
use glib::{g_main_context_ref, g_main_context_unref};
use gobject::{g_object_ref,g_object_unref};

pub trait GMainContext { }

pub struct Object<R> {
    priv wrapped: *R,
    priv context: *GMainContext
}

pub unsafe fn take_object<R>(obj: *R, ctx: *GMainContext) -> Object<R> {
    debug!("task %d: taking object %? (ref context %?)",
           *task::get_task(), obj, ctx);
    Object { wrapped: obj, context: g_main_context_ref(ctx) }
}

impl<R> Object<R> {
    pub unsafe fn raw(&self) -> *R { self.wrapped }
    pub unsafe fn context(&self) -> *GMainContext { self.context }
}

extern fn call_on_stack_cb(data: *(), ctx: *GMainContext) {
    unsafe {
        let func = *(data as *&fn(*GMainContext));
        func(ctx);
    }
}

extern fn call_off_stack_cb(data: *(), ctx: *GMainContext) {
    unsafe {
        let func = *(data as *~fn(*GMainContext));
        func(ctx);
    }
}

pub unsafe fn call_on_stack(ctx: *GMainContext, func: &fn(*GMainContext))
        -> bool {
    return grustna_call_on_stack(call_on_stack_cb,
                                 ptr::to_unsafe_ptr(&func) as *(),
                                 ctx)
           as bool;
}

pub unsafe fn call_off_stack(ctx: *GMainContext, func: ~fn(*GMainContext)) {
    if !(grustna_call_off_stack(call_off_stack_cb,
                                ptr::to_unsafe_ptr(&func) as *(),
                                ctx)
         as bool) {
        fail!();
    }
}

#[unsafe_destructor]
impl<R> Drop for Object<R> {
    fn finalize(&self) {
        unsafe {
            debug!("task %d: finalize (unref context %?) unref object %?",
                   *task::get_task(), self.context, self.wrapped);
            g_main_context_unref(self.context);
            g_object_unref(self.wrapped as *());
        }
    }
}

impl<R> Clone for Object<R> {
    fn clone(&self) -> Object<R> {
        unsafe {
            debug!("task %d: clone ref object %?", *task::get_task(), self.wrapped);
            g_object_ref(self.wrapped as *());
            take_object(self.wrapped, self.context)
        }
    }
}
<commit_msg>More informative call timeout failure<commit_after>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

use na::{grustna_call_on_stack,grustna_call_off_stack};
use glib::{g_main_context_ref, g_main_context_unref};
use gobject::{g_object_ref,g_object_unref};

pub trait GMainContext { }

pub struct Object<R> {
    priv wrapped: *R,
    priv context: *GMainContext
}

pub unsafe fn take_object<R>(obj: *R, ctx: *GMainContext) -> Object<R> {
    debug!("task %d: taking object %? (ref context %?)",
           *task::get_task(), obj, ctx);
    Object { wrapped: obj, context: g_main_context_ref(ctx) }
}

impl<R> Object<R> {
    pub unsafe fn raw(&self) -> *R { self.wrapped }
    pub unsafe fn context(&self) -> *GMainContext { self.context }
}

extern fn call_on_stack_cb(data: *(), ctx: *GMainContext) {
    unsafe {
        let func = *(data as *&fn(*GMainContext));
        func(ctx);
    }
}

extern fn call_off_stack_cb(data: *(), ctx: *GMainContext) {
    unsafe {
        let func = *(data as *~fn(*GMainContext));
        func(ctx);
    }
}

pub unsafe fn call_on_stack(ctx: *GMainContext, func: &fn(*GMainContext))
        -> bool {
    return grustna_call_on_stack(call_on_stack_cb,
                                 ptr::to_unsafe_ptr(&func) as *(),
                                 ctx)
           as bool;
}

pub unsafe fn call_off_stack(ctx: *GMainContext, func: ~fn(*GMainContext)) {
    if !(grustna_call_off_stack(call_off_stack_cb,
                                ptr::to_unsafe_ptr(&func) as *(),
                                ctx)
         as bool) {
        fail!(~"off-stack call failure");
    }
}

#[unsafe_destructor]
impl<R> Drop for Object<R> {
    fn finalize(&self) {
        unsafe {
            debug!("task %d: finalize (unref context %?) unref object %?",
                   *task::get_task(), self.context, self.wrapped);
            g_main_context_unref(self.context);
            g_object_unref(self.wrapped as *());
        }
    }
}

impl<R> Clone for Object<R> {
    fn clone(&self) -> Object<R> {
        unsafe {
            debug!("task %d: clone ref object %?", *task::get_task(), self.wrapped);
            g_object_ref(self.wrapped as *());
            take_object(self.wrapped, self.context)
        }
    }
}
<|endoftext|>


<commit_before>trait Mbc {
    fn read(&self, address: u16) -> u8;
    fn write(&mut self, address: u16, v: u8);
}

struct Mbc3 {
    selected_bank: usize,
    data: Vec<u8>,
    offset: usize,
    ram: [u8; 24576], // Up to 3 x 8KB banks

    ram_offset: usize,
    ram_enabled: bool,
}

impl Mbc3 {
    pub fn new(data: Vec<u8>) -> Mbc3 {
        Mbc3 {
            selected_bank: 1,
            data: data,
            offset: 0,
            ram: [0; 24576],

            ram_offset: 0,
            ram_enabled: false,
        }
    }

    fn write_ram(&mut self, address: u16, v: u8) {
        if !self.ram_enabled {
            panic!("MBC3 RAM has not been enabled.");
        }

        self.ram[(address - 0xA000) as usize + self.ram_offset] = v;
    }

    fn read_ram(&self, address: u16) -> u8 {
        if !self.ram_enabled {
            panic!("MBC3 RAM has not been enabled.");
        }

        self.ram[(address - 0xA000) as usize + self.ram_offset]
    }
}

impl Mbc for Mbc3 {
    fn read(&self, address: u16) -> u8 {
        match address {
            0x0000 ... 0x3FFF => self.data[address as usize],
            0x4000 ... 0x7FFF => self.data[address as usize + self.offset],
            0xA000 ... 0xBFFF => self.read_ram(address),
            _ => unimplemented!(),
        }
    }

    fn write(&mut self, address: u16, v: u8) {
        // println!("Attempt to write {:02X} to {:04X}", v, address);
        match address {
            0x0000 ... 0x1FFF => {
                match v {
                    0x00 => self.ram_enabled = false,
                    0x0A => self.ram_enabled = true,
                    _    => panic!("Unrecognized value for RAM enablement."),
                }
            },
            0x2000 ... 0x3FFF => {
                // Any write to this area will enable the bank of memory contained in v
                // if 0 is selected we will select 1 because 0 is already available
                // statically in 0 0000 - 3FFF
                if v > 0 {
                    self.offset = ((v - 1) as usize) * 0x4000;
                } else {
                    self.offset = 0x0000;
                }
                // println!("Setting offset to {:06X}", self.offset);
            },
            0x4000 ... 0x5FFF => {
                match v {
                    0x00 => self.ram_offset = 0,
                    0x01 => self.ram_offset = 8192,
                    0x02 => self.ram_offset = 16384,
                    _ => unimplemented!(),
                }
            },
            0xA000 ... 0xBFFF => { self.write_ram(address, v); },
            _ => unimplemented!(),
        }
    }
}

pub struct MemoryController {
    controller: Box<Mbc>,
}

impl MemoryController {
    pub fn from_bytes(bytes: Vec<u8>) -> MemoryController {
        let controller = match bytes[0x147] {
            0x0F | 0x10 | 0x11 | 0x12 | 0x13 | 0x01 => Box::new(Mbc3::new(bytes)),
            _ => {
                println!("Unrecognized type {:02X}", bytes[0x147]);
                panic!();
            }
        };

        MemoryController {
            controller: controller,
        }
    }

    pub fn read(&self, address: u16) -> u8 {
        self.controller.read(address)
    }

    pub fn write(&mut self, address: u16, v: u8) {
        self.controller.write(address, v);
    }
}
<commit_msg>Add support for 'MBC0' cartridges.<commit_after>trait Mbc {
    fn read(&self, address: u16) -> u8;
    fn write(&mut self, address: u16, v: u8);
}

struct Mbc0 {
    data: Vec<u8>,
    ram: [u8; 8192],
}

impl Mbc0 {
    pub fn new(data: Vec<u8>) -> Mbc0 {
        Mbc0 {
            data: data,
            ram: [0; 8192],
        }
    }
}

impl Mbc for Mbc0 {
    fn read(&self, address: u16) -> u8 {
        match address {
            0x0000 ... 0x7FFF => self.data[address as usize],
            0xA000 ... 0xBFFF => self.ram[(address as usize) - 0xA000],
            _ => unimplemented!(),
        }
    }

    fn write(&mut self, address: u16, v: u8) {
        println!("Writing to {:04X}", address);
        match address {
            0xA000 ... 0xBFFF => self.ram[(address as usize) - 0xA000] = v,
            _ => unimplemented!(),
        }
    }
}

struct Mbc3 {
    selected_bank: usize,
    data: Vec<u8>,
    offset: usize,
    ram: [u8; 24576], // Up to 3 x 8KB banks

    ram_offset: usize,
    ram_enabled: bool,
}

impl Mbc3 {
    pub fn new(data: Vec<u8>) -> Mbc3 {
        Mbc3 {
            selected_bank: 1,
            data: data,
            offset: 0,
            ram: [0; 24576],

            ram_offset: 0,
            ram_enabled: false,
        }
    }

    fn write_ram(&mut self, address: u16, v: u8) {
        if !self.ram_enabled {
            panic!("MBC3 RAM has not been enabled.");
        }

        self.ram[(address - 0xA000) as usize + self.ram_offset] = v;
    }

    fn read_ram(&self, address: u16) -> u8 {
        if !self.ram_enabled {
            panic!("MBC3 RAM has not been enabled.");
        }

        self.ram[(address - 0xA000) as usize + self.ram_offset]
    }
}

impl Mbc for Mbc3 {
    fn read(&self, address: u16) -> u8 {
        match address {
            0x0000 ... 0x3FFF => self.data[address as usize],
            0x4000 ... 0x7FFF => self.data[address as usize + self.offset],
            0xA000 ... 0xBFFF => self.read_ram(address),
            _ => unimplemented!(),
        }
    }

    fn write(&mut self, address: u16, v: u8) {
        // println!("Attempt to write {:02X} to {:04X}", v, address);
        match address {
            0x0000 ... 0x1FFF => {
                match v {
                    0x00 => self.ram_enabled = false,
                    0x0A => self.ram_enabled = true,
                    _    => panic!("Unrecognized value for RAM enablement."),
                }
            },
            0x2000 ... 0x3FFF => {
                // Any write to this area will enable the bank of memory contained in v
                // if 0 is selected we will select 1 because 0 is already available
                // statically in 0 0000 - 3FFF
                if v > 0 {
                    self.offset = ((v - 1) as usize) * 0x4000;
                } else {
                    self.offset = 0x0000;
                }
                // println!("Setting offset to {:06X}", self.offset);
            },
            0x4000 ... 0x5FFF => {
                match v {
                    0x00 => self.ram_offset = 0,
                    0x01 => self.ram_offset = 8192,
                    0x02 => self.ram_offset = 16384,
                    _ => unimplemented!(),
                }
            },
            0xA000 ... 0xBFFF => { self.write_ram(address, v); },
            _ => unimplemented!(),
        }
    }
}

pub struct MemoryController {
    controller: Box<Mbc>,
}

impl MemoryController {
    pub fn from_bytes(bytes: Vec<u8>) -> MemoryController {
        let controller = match bytes[0x147] {
            0x00 => Box::new(Mbc0::new(bytes)) as Box<Mbc>,
            0x0F | 0x10 | 0x11 | 0x12 | 0x13 | 0x01 => Box::new(Mbc3::new(bytes)) as Box<Mbc>,
            _ => {
                println!("Unrecognized type {:02X}", bytes[0x147]);
                panic!();
            }
        };

        MemoryController {
            controller: controller,
        }
    }

    pub fn read(&self, address: u16) -> u8 {
        self.controller.read(address)
    }

    pub fn write(&mut self, address: u16, v: u8) {
        self.controller.write(address, v);
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, HashSet, VecDeque};

#[derive(Debug)]
struct OrbitMap<'a> {
  objects: HashSet<&'a str>,
  parents: HashMap<&'a str, &'a str>,
  children: HashMap<&'a str, Vec<&'a str>>
}

impl<'a> OrbitMap<'a> {
  fn new() -> OrbitMap<'a> {
    OrbitMap {
      objects: HashSet::new(),
      parents: HashMap::new(),
      children: HashMap::new()
    }
  }

  fn insert(&mut self, parent: &'a str, child: &'a str) {
    self.objects.insert(parent);
    self.objects.insert(child);
    self.parents.insert(child, parent);

    if let Some(children) = self.children.get_mut(parent) {
      children.push(child);
    } else {
      self.children.insert(parent, vec![child]);
    }
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("06.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_input<'a>(input: &'a str) -> OrbitMap<'a> {
  input.lines()
  .filter_map(|line| {
    let mut ids = line.split(')');

    match (ids.nth(0), ids.nth(0)) {
      (Some(parent), Some(child)) => Some((parent, child)),
      _ => None
    }
  })
  .fold(OrbitMap::new(), |mut orbit_map, (parent, child)| {
    orbit_map.insert(parent, child);
    orbit_map
  })
}

fn get_ancestors<'a>(orbit_map: &'a OrbitMap, object: &'a str) -> Vec<&'a str> {
  let mut result = vec![];
  let mut object = object;

  while let Some(&parent) = orbit_map.parents.get(object) {
    result.push(parent);
    object = parent;
  }

  result
}

fn bfs<'a>(orbit_map: &'a OrbitMap, start: &'a str, target: &'a str) -> Option<Vec<&'a str>> {
  let mut previous_map = HashMap::new();
  previous_map.insert(start, None);

  let mut queue = VecDeque::new();
  queue.push_back(start);

  while let Some(current) = queue.pop_front() {
    if current == target {
      break;
    }

    let parent_list = [orbit_map.parents.get(current)];
    let next_list = orbit_map.children.get(current)
      .map(|children| children.iter().chain(
        parent_list.into_iter().filter_map(|&x| x)
      ));

    if let Some(next_list) = next_list {
      for &next in next_list {
        if !previous_map.contains_key(next) {
          previous_map.insert(next, Some(current));
          queue.push_back(next);
        }
      }
    }
  }

  if let None = previous_map.get(target) {
    return None;
  }

  let mut path = vec![target];

  while let Some(&Some(previous)) = previous_map.get(path.last().unwrap()) {
    path.push(previous);
  }

  path.reverse();
  Some(path)
}

fn main() {
  let input = get_input().unwrap();
  let orbit_map = parse_input(&input);

  let total_ancestors = orbit_map.objects.iter()
    .map(|&object| get_ancestors(&orbit_map, object).len())
    .sum::<usize>();

  println!("Part 1: {}", total_ancestors);

  let me_orbit = orbit_map.parents["YOU"];
  let santa_orbit = orbit_map.parents["SAN"];
  let path = bfs(&orbit_map, me_orbit, santa_orbit).unwrap();

  println!("Part 2: {}", path.len() - 1);
}
<commit_msg>Use of generics instead of lifetimes<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::Hash;

#[derive(Debug)]
struct OrbitMap<T: Hash + Eq> {
  objects: HashSet<T>,
  parents: HashMap<T, T>,
  children: HashMap<T, Vec<T>>
}

impl<T: Copy + Hash + Eq> OrbitMap<T> {
  fn new() -> OrbitMap<T> {
    OrbitMap {
      objects: HashSet::new(),
      parents: HashMap::new(),
      children: HashMap::new()
    }
  }

  fn insert(&mut self, parent: T, child: T) {
    self.objects.insert(parent);
    self.objects.insert(child);
    self.parents.insert(child, parent);

    if let Some(children) = self.children.get_mut(&parent) {
      children.push(child);
    } else {
      self.children.insert(parent, vec![child]);
    }
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("06.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_input(input: &str) -> OrbitMap<&str> {
  input.lines()
  .filter_map(|line| {
    let mut ids = line.split(')');

    match (ids.nth(0), ids.nth(0)) {
      (Some(parent), Some(child)) => Some((parent, child)),
      _ => None
    }
  })
  .fold(OrbitMap::new(), |mut orbit_map, (parent, child)| {
    orbit_map.insert(parent, child);
    orbit_map
  })
}

fn get_orbits<T: Copy + Hash + Eq>(orbit_map: &OrbitMap<T>, object: T) -> Vec<T> {
  let mut result = vec![];
  let mut object = object;

  while let Some(&parent) = orbit_map.parents.get(&object) {
    result.push(parent);
    object = parent;
  }

  result
}

fn bfs<T: Copy + Hash + Eq>(orbit_map: &OrbitMap<T>, start: T, target: T) -> Option<Vec<T>> {
  let mut previous_map = HashMap::new();
  previous_map.insert(start, None);

  let mut queue = VecDeque::new();
  queue.push_back(start);

  while let Some(current) = queue.pop_front() {
    if current == target {
      break;
    }

    let parent_list = [orbit_map.parents.get(&current)];
    let next_list = orbit_map.children.get(&current)
      .map(|children| children.iter().chain(
        parent_list.into_iter().filter_map(|&x| x)
      ));

    if let Some(next_list) = next_list {
      for &next in next_list {
        if !previous_map.contains_key(&next) {
          previous_map.insert(next, Some(current));
          queue.push_back(next);
        }
      }
    }
  }

  if let None = previous_map.get(&target) {
    return None;
  }

  let mut path = vec![target];

  while let Some(&Some(previous)) = previous_map.get(path.last().unwrap()) {
    path.push(previous);
  }

  path.reverse();
  Some(path)
}

fn main() {
  let input = get_input().unwrap();
  let orbit_map = parse_input(&input);

  let total_ancestors = orbit_map.objects.iter()
    .map(|&object| get_orbits(&orbit_map, object).len())
    .sum::<usize>();

  println!("Part 1: {}", total_ancestors);

  let me_orbit = orbit_map.parents["YOU"];
  let santa_orbit = orbit_map.parents["SAN"];
  let path = bfs(&orbit_map, me_orbit, santa_orbit).unwrap();

  println!("Part 2: {}", path.len() - 1);
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::VecDeque;
use std::env::{current_dir, home_dir, set_current_dir};
use std::path::PathBuf;
use super::status::{FAILURE, SUCCESS};
use super::variables::Variables;

pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    /// Create a new `DirectoryStack` containing the current working directory,
    /// if available.
    pub(crate) fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match current_dir() {
            Ok(curr_dir) => {
                dirs.push_front(curr_dir);
                DirectoryStack { dirs: dirs }
            }
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                DirectoryStack { dirs: dirs }
            }
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of
    /// the
    /// directory stack size variable. If it succeeds, it will return the value of that
    /// variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables
            .get_var_or_empty("DIRECTORY_STACK_SIZE")
            .parse::<usize>()
            .unwrap_or(1000)
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub(crate) fn popd<I: IntoIterator>(&mut self, args: I, variables: &mut Variables) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        let mut keep_front = false; // whether the -n option is present
        let mut count_from_front = true; // <=> input number is positive
        let mut num: usize = 0;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            if arg == "-n" {
                keep_front = true;
            } else {
                match parse_numeric_arg(arg) {
                    Some((x, y)) => {
                        count_from_front = x;
                        num = y;
                    }
                    None => {
                        return Err(Cow::Owned(format!(
                            "ion: popd: {}: invalid argument\n",
                            arg
                        )))
                    }
                };
            }
        }

        let len: usize = self.dirs.len();
        if len <= 1 {
            return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
        }

        let mut index: usize = if count_from_front {
            num
        } else {
            (len - 1).checked_sub(num).ok_or_else(|| {
                Cow::Owned(format!(
                    "ion: popd: negative directory stack index out of range\n"
                ))
            })?
        };

        // apply -n
        if index == 0 && keep_front {
            index = 1;
        }

        // change to new directory, return if not possible
        if index == 0 {
            self.set_current_dir_by_index(1, "popd")?;
        }

        // pop element
        if self.dirs.remove(index).is_none() {
            return Err(Cow::Owned(format!(
                "ion: popd: {}: directory stack index out of range\n",
                index
            )));
        }

        // Manually update $PWD
        variables.set_var("PWD", current_dir().unwrap().to_str().unwrap());
        self.print_dirs();
        Ok(())
    }

    pub(crate) fn pushd<I: IntoIterator>(
        &mut self,
        args: I,
        variables: &mut Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        enum Action {
            Switch,          // <no arguments>
            RotLeft(usize),  // +[num]
            RotRight(usize), // -[num]
            Push(PathBuf),   // [dir]
        }

        let mut keep_front = false; // whether the -n option is present
        let mut action: Action = Action::Switch;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            if arg == "-n" {
                keep_front = true;
            } else if let Action::Switch = action {
                // if action is not yet defined
                action = match parse_numeric_arg(arg) {
                    Some((true, num)) => Action::RotLeft(num),
                    Some((false, num)) => Action::RotRight(num),
                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
                };
            } else {
                return Err(Cow::Borrowed("ion: pushd: too many arguments\n"));
            }
        }

        let len = self.dirs.len();
        match action {
            Action::Switch => {
                if len < 2 {
                    return Err(Cow::Borrowed("ion: pushd: no other directory\n"));
                }
                if !keep_front {
                    self.set_current_dir_by_index(1, "pushd")?;
                    self.dirs.swap(0, 1);
                }
            }
            Action::RotLeft(num) => if !keep_front {
                self.set_current_dir_by_index(num, "pushd")?;
                self.rotate_left(num);
            },
            Action::RotRight(num) => if !keep_front {
                self.set_current_dir_by_index(len - (num % len), "pushd")?;
                self.rotate_right(num);
            },
            Action::Push(dir) => {
                let index = if keep_front { 1 } else { 0 };
                self.insert_dir(index, dir, variables);
            }
        };

        // Manually update $PWD
        variables.set_var("PWD", current_dir().unwrap().to_str().unwrap());
        self.print_dirs();
        Ok(())
    }

    pub(crate) fn cd<I: IntoIterator>(
        &mut self,
        args: I,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        match args.into_iter().nth(1) {
            Some(dir) => {
                let dir = dir.as_ref();
                if dir == "-" {
                    self.switch_to_previous_directory(variables)
                } else {
                    self.change_and_push_dir(dir, variables)
                }
            }
            None => self.switch_to_home_directory(variables),
        }
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        home_dir().map_or(
            Err(Cow::Borrowed("ion: failed to get home directory")),
            |home| {
                home.to_str().map_or(
                    Err(Cow::Borrowed(
                        "ion: failed to convert home directory to str",
                    )),
                    |home| self.change_and_push_dir(home, variables),
                )
            },
        )
    }

    fn switch_to_previous_directory(
        &mut self,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir().cloned().map_or_else(
            || Err(Cow::Borrowed("ion: no previous directory to switch to")),
            |prev| {
                self.dirs.remove(1);
                let prev = prev.to_string_lossy().to_string();
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            },
        )
    }

    fn get_previous_dir(&self) -> Option<&PathBuf> {
        if self.dirs.len() < 2 {
            None
        } else {
            self.dirs.get(1)
        }
    }

    pub(crate) fn change_and_push_dir(
        &mut self,
        dir: &str,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        match (set_current_dir(dir), current_dir()) {
            (Ok(()), Ok(cur_dir)) => {
                self.push_dir(cur_dir, variables);
                Ok(())
            }
            (Err(err), _) => Err(Cow::Owned(format!(
                "ion: failed to set current dir to {}: {}\n",
                dir, err
            ))),
            (..) => Err(Cow::Borrowed(
                "ion: change_and_push_dir(): error occurred that should never happen\n",
            )), // This should not happen
        }
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
        self.dirs.insert(index, path);
        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub(crate) fn dirs<I: IntoIterator>(&mut self, args: I) -> i32
    where
        I::Item: AsRef<str>,
    {
        const CLEAR: u8 = 1; // -c
        const ABS_PATHNAMES: u8 = 2; // -l
        const MULTILINE: u8 = 4; // -p | -v
        const INDEX: u8 = 8; // -v

        let mut dirs_args: u8 = 0;
        let mut num_arg: Option<usize> = None;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            match arg {
                "-c" => dirs_args |= CLEAR,
                "-l" => dirs_args |= ABS_PATHNAMES,
                "-p" => dirs_args |= MULTILINE,
                "-v" => dirs_args |= INDEX | MULTILINE,
                arg => {
                    num_arg = match parse_numeric_arg(arg) {
                        Some((true, num)) => Some(num),
                        Some((false, num)) if self.dirs.len() > num => {
                            Some(self.dirs.len() - num - 1)
                        }
                        _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
                                              * argument\n", arg))) */
                    };
                }
            }
        }

        if dirs_args & CLEAR > 0 {
            self.dirs.truncate(1);
        }

        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (
            dirs_args & ABS_PATHNAMES > 0,
            dirs_args & INDEX > 0,
        ) {
            // ABS, INDEX
            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
            (true, false) => |(_, x)| try_abs_path(x),
            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
            (false, false) => |(_, x)| x.to_string_lossy(),
        };

        let mut iter = self.dirs.iter().enumerate().map(mapper);

        if let Some(num) = num_arg {
            match iter.nth(num) {
                Some(x) => println!("{}", x),
                None => return FAILURE,
            };
        } else {
            let folder: fn(String, Cow<str>) -> String = match dirs_args & MULTILINE > 0 {
                true => |x, y| x + "\n" + &y,
                false => |x, y| x + " " + &y,
            };

            let first = match iter.next() {
                Some(x) => x.to_string(),
                None => return SUCCESS,
            };

            println!("{}", iter.fold(first, folder));
        }
        SUCCESS
    }

    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }

    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_left());
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(
        &self,
        index: usize,
        caller: &str,
    ) -> Result<(), Cow<'static, str>> {
        let dir = self.dirs.iter().nth(index).ok_or_else(|| {
            Cow::Owned(format!(
                "ion: {}: {}: directory stack out of range\n",
                caller, index
            ))
        })?;

        set_current_dir(dir)
            .map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
    }

    // pushd +<num>
    fn rotate_left(&mut self, num: usize) {
        let cloned = self.dirs.clone();
        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
            *dest = src.clone();
        }
    }

    // pushd -<num>
    fn rotate_right(&mut self, num: usize) {
        let len = self.dirs.len();
        self.rotate_left(len - (num % len));
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
    match arg.chars().nth(0) {
        Some('+') => Some(true),
        Some('-') => Some(false),
        _ => None,
    }.and_then(|b| arg[1..].parse::<usize>().ok().map(|num| (b, num)))
}

// converts pbuf to an absolute path if possible
fn try_abs_path(pbuf: &PathBuf) -> Cow<str> {
    Cow::Owned(
        pbuf.canonicalize()
            .unwrap_or_else(|_| pbuf.clone())
            .to_string_lossy()
            .to_string(),
    )
}
<commit_msg>Fix cd'ing into a symlinked directory losing the original path<commit_after>use std::borrow::Cow;
use std::collections::VecDeque;
use std::env::{current_dir, home_dir, set_current_dir};
use std::path::PathBuf;
use super::status::{FAILURE, SUCCESS};
use super::variables::Variables;

pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    /// Create a new `DirectoryStack` containing the current working directory,
    /// if available.
    pub(crate) fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match current_dir() {
            Ok(curr_dir) => {
                dirs.push_front(curr_dir);
                DirectoryStack { dirs: dirs }
            }
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                DirectoryStack { dirs: dirs }
            }
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of
    /// the
    /// directory stack size variable. If it succeeds, it will return the value of that
    /// variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables
            .get_var_or_empty("DIRECTORY_STACK_SIZE")
            .parse::<usize>()
            .unwrap_or(1000)
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub(crate) fn popd<I: IntoIterator>(&mut self, args: I, variables: &mut Variables) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        let mut keep_front = false; // whether the -n option is present
        let mut count_from_front = true; // <=> input number is positive
        let mut num: usize = 0;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            if arg == "-n" {
                keep_front = true;
            } else {
                match parse_numeric_arg(arg) {
                    Some((x, y)) => {
                        count_from_front = x;
                        num = y;
                    }
                    None => {
                        return Err(Cow::Owned(format!(
                            "ion: popd: {}: invalid argument\n",
                            arg
                        )))
                    }
                };
            }
        }

        let len: usize = self.dirs.len();
        if len <= 1 {
            return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
        }

        let mut index: usize = if count_from_front {
            num
        } else {
            (len - 1).checked_sub(num).ok_or_else(|| {
                Cow::Owned(format!(
                    "ion: popd: negative directory stack index out of range\n"
                ))
            })?
        };

        // apply -n
        if index == 0 && keep_front {
            index = 1;
        }

        // change to new directory, return if not possible
        if index == 0 {
            self.set_current_dir_by_index(1, "popd")?;
        }

        // pop element
        if self.dirs.remove(index).is_none() {
            return Err(Cow::Owned(format!(
                "ion: popd: {}: directory stack index out of range\n",
                index
            )));
        }

        // Manually update $PWD
        variables.set_var("PWD", current_dir().unwrap().to_str().unwrap());
        self.print_dirs();
        Ok(())
    }

    pub(crate) fn pushd<I: IntoIterator>(
        &mut self,
        args: I,
        variables: &mut Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        enum Action {
            Switch,          // <no arguments>
            RotLeft(usize),  // +[num]
            RotRight(usize), // -[num]
            Push(PathBuf),   // [dir]
        }

        let mut keep_front = false; // whether the -n option is present
        let mut action: Action = Action::Switch;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            if arg == "-n" {
                keep_front = true;
            } else if let Action::Switch = action {
                // if action is not yet defined
                action = match parse_numeric_arg(arg) {
                    Some((true, num)) => Action::RotLeft(num),
                    Some((false, num)) => Action::RotRight(num),
                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
                };
            } else {
                return Err(Cow::Borrowed("ion: pushd: too many arguments\n"));
            }
        }

        let len = self.dirs.len();
        match action {
            Action::Switch => {
                if len < 2 {
                    return Err(Cow::Borrowed("ion: pushd: no other directory\n"));
                }
                if !keep_front {
                    self.set_current_dir_by_index(1, "pushd")?;
                    self.dirs.swap(0, 1);
                }
            }
            Action::RotLeft(num) => if !keep_front {
                self.set_current_dir_by_index(num, "pushd")?;
                self.rotate_left(num);
            },
            Action::RotRight(num) => if !keep_front {
                self.set_current_dir_by_index(len - (num % len), "pushd")?;
                self.rotate_right(num);
            },
            Action::Push(dir) => {
                let index = if keep_front { 1 } else { 0 };
                self.insert_dir(index, dir, variables);
            }
        };

        // Manually update $PWD
        variables.set_var("PWD", current_dir().unwrap().to_str().unwrap());
        self.print_dirs();
        Ok(())
    }

    pub(crate) fn cd<I: IntoIterator>(
        &mut self,
        args: I,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        I::Item: AsRef<str>,
    {
        match args.into_iter().nth(1) {
            Some(dir) => {
                let dir = dir.as_ref();
                if dir == "-" {
                    self.switch_to_previous_directory(variables)
                } else {
                    self.change_and_push_dir(dir, variables)
                }
            }
            None => self.switch_to_home_directory(variables),
        }
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        home_dir().map_or(
            Err(Cow::Borrowed("ion: failed to get home directory")),
            |home| {
                home.to_str().map_or(
                    Err(Cow::Borrowed(
                        "ion: failed to convert home directory to str",
                    )),
                    |home| self.change_and_push_dir(home, variables),
                )
            },
        )
    }

    fn switch_to_previous_directory(
        &mut self,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir().cloned().map_or_else(
            || Err(Cow::Borrowed("ion: no previous directory to switch to")),
            |prev| {
                self.dirs.remove(1);
                let prev = prev.to_string_lossy().to_string();
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            },
        )
    }

    fn get_previous_dir(&self) -> Option<&PathBuf> {
        if self.dirs.len() < 2 {
            None
        } else {
            self.dirs.get(1)
        }
    }

    pub(crate) fn change_and_push_dir(
        &mut self,
        dir: &str,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        use std::path::{Component, Path};

        // Create a clone of the current directory.
        let mut new_dir = match self.dirs.front() {
            Some(cur_dir) => cur_dir.clone(),
            None => PathBuf::new()
        };

        // Iterate through components of the specified directory
        // and calculate the new path based on them.
        for component in Path::new(dir).components() {
            match component {
                Component::CurDir => { },
                Component::ParentDir => { new_dir.pop(); },
                _ => { new_dir.push(component); }
            };
        }

        // Try to change into the new directory
        match set_current_dir(&new_dir) {
            Ok(()) => {
                // Push the new current directory onto the directory stack.
                self.push_dir(new_dir, variables);
                Ok(())
            }
            Err(err) => Err(Cow::Owned(format!(
                "ion: failed to set current dir to {}: {}\n",
                new_dir.to_string_lossy(), err
            )))
        }
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
        self.dirs.insert(index, path);
        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub(crate) fn dirs<I: IntoIterator>(&mut self, args: I) -> i32
    where
        I::Item: AsRef<str>,
    {
        const CLEAR: u8 = 1; // -c
        const ABS_PATHNAMES: u8 = 2; // -l
        const MULTILINE: u8 = 4; // -p | -v
        const INDEX: u8 = 8; // -v

        let mut dirs_args: u8 = 0;
        let mut num_arg: Option<usize> = None;

        for arg in args.into_iter().skip(1) {
            let arg = arg.as_ref();
            match arg {
                "-c" => dirs_args |= CLEAR,
                "-l" => dirs_args |= ABS_PATHNAMES,
                "-p" => dirs_args |= MULTILINE,
                "-v" => dirs_args |= INDEX | MULTILINE,
                arg => {
                    num_arg = match parse_numeric_arg(arg) {
                        Some((true, num)) => Some(num),
                        Some((false, num)) if self.dirs.len() > num => {
                            Some(self.dirs.len() - num - 1)
                        }
                        _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
                                              * argument\n", arg))) */
                    };
                }
            }
        }

        if dirs_args & CLEAR > 0 {
            self.dirs.truncate(1);
        }

        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (
            dirs_args & ABS_PATHNAMES > 0,
            dirs_args & INDEX > 0,
        ) {
            // ABS, INDEX
            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
            (true, false) => |(_, x)| try_abs_path(x),
            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
            (false, false) => |(_, x)| x.to_string_lossy(),
        };

        let mut iter = self.dirs.iter().enumerate().map(mapper);

        if let Some(num) = num_arg {
            match iter.nth(num) {
                Some(x) => println!("{}", x),
                None => return FAILURE,
            };
        } else {
            let folder: fn(String, Cow<str>) -> String = match dirs_args & MULTILINE > 0 {
                true => |x, y| x + "\n" + &y,
                false => |x, y| x + " " + &y,
            };

            let first = match iter.next() {
                Some(x) => x.to_string(),
                None => return SUCCESS,
            };

            println!("{}", iter.fold(first, folder));
        }
        SUCCESS
    }

    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }

    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_left());
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(
        &self,
        index: usize,
        caller: &str,
    ) -> Result<(), Cow<'static, str>> {
        let dir = self.dirs.iter().nth(index).ok_or_else(|| {
            Cow::Owned(format!(
                "ion: {}: {}: directory stack out of range\n",
                caller, index
            ))
        })?;

        set_current_dir(dir)
            .map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
    }

    // pushd +<num>
    fn rotate_left(&mut self, num: usize) {
        let cloned = self.dirs.clone();
        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
            *dest = src.clone();
        }
    }

    // pushd -<num>
    fn rotate_right(&mut self, num: usize) {
        let len = self.dirs.len();
        self.rotate_left(len - (num % len));
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
    match arg.chars().nth(0) {
        Some('+') => Some(true),
        Some('-') => Some(false),
        _ => None,
    }.and_then(|b| arg[1..].parse::<usize>().ok().map(|num| (b, num)))
}

// converts pbuf to an absolute path if possible
fn try_abs_path(pbuf: &PathBuf) -> Cow<str> {
    Cow::Owned(
        pbuf.canonicalize()
            .unwrap_or_else(|_| pbuf.clone())
            .to_string_lossy()
            .to_string(),
    )
}
<|endoftext|>


<commit_before>//! Basic syntax highlighting functionality.
//!
//! This module uses libsyntax's lexer to provide token-based highlighting for
//! the HTML documentation generated by rustdoc.
//!
//! Use the `render_with_highlighting` to highlight some rust code.

use html::escape::Escape;

use std::fmt::Display;
use std::io;
use std::io::prelude::*;

use syntax::source_map::{SourceMap, FilePathMapping};
use syntax::parse::lexer::{self, TokenAndSpan};
use syntax::parse::token;
use syntax::parse;
use syntax_pos::{Span, FileName};

/// Highlights `src`, returning the HTML output.
pub fn render_with_highlighting(
    src: &str,
    class: Option<&str>,
    extension: Option<&str>,
    tooltip: Option<(&str, &str)>,
) -> String {
    debug!("highlighting: ================\n{}\n==============", src);
    let sess = parse::ParseSess::new(FilePathMapping::empty());
    let fm = sess.source_map().new_source_file(FileName::Custom("stdin".to_string()),
                                               src.to_string());

    let mut out = Vec::new();
    if let Some((tooltip, class)) = tooltip {
        write!(out, "

ⓘ{}

",
               class, tooltip).unwrap();
    }
    write_header(class, &mut out).unwrap();

    let lexer = match lexer::StringReader::new_without_err(&sess, fm, None, "Output from rustc:") {
        Ok(l) => l,
        Err(_) => {
            let first_line = src.lines().next().unwrap_or_else(|| "");
            let mut err = sess.span_diagnostic
                              .struct_warn(&format!("Invalid doc comment starting with: `{}`\n\
                                                     (Ignoring this codeblock)",
                                                    first_line));
            err.emit();
            return String::new();
        }
    };
    let mut classifier = Classifier::new(lexer, sess.source_map());
    if classifier.write_source(&mut out).is_err() {
        classifier.lexer.emit_fatal_errors();
        return format!("
{}
", src);
    }

    if let Some(extension) = extension {
        write!(out, "{}", extension).unwrap();
    }
    write_footer(&mut out).unwrap();
    String::from_utf8_lossy(&out[..]).into_owned()
}

/// Processes a program (nested in the internal `lexer`), classifying strings of
/// text by highlighting category (`Class`). Calls out to a `Writer` to write
/// each span of text in sequence.
struct Classifier<'a> {
    lexer: lexer::StringReader<'a>,
    source_map: &'a SourceMap,

    // State of the classifier.
    in_attribute: bool,
    in_macro: bool,
    in_macro_nonterminal: bool,
}

/// How a span of text is classified. Mostly corresponds to token kinds.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Class {
    None,
    Comment,
    DocComment,
    Attribute,
    KeyWord,
    // Keywords that do pointer/reference stuff.
    RefKeyWord,
    Self_,
    Op,
    Macro,
    MacroNonTerminal,
    String,
    Number,
    Bool,
    Ident,
    Lifetime,
    PreludeTy,
    PreludeVal,
    QuestionMark,
}

/// Trait that controls writing the output of syntax highlighting. Users should
/// implement this trait to customize writing output.
///
/// The classifier will call into the `Writer` implementation as it finds spans
/// of text to highlight. Exactly how that text should be highlighted is up to
/// the implementation.
trait Writer {
    /// Called when we start processing a span of text that should be highlighted.
    /// The `Class` argument specifies how it should be highlighted.
    fn enter_span(&mut self, _: Class) -> io::Result<()>;

    /// Called at the end of a span of highlighted text.
    fn exit_span(&mut self) -> io::Result<()>;

    /// Called for a span of text.  If the text should be highlighted differently from the
    /// surrounding text, then the `Class` argument will be a value other than `None`.
    ///
    /// The following sequences of callbacks are equivalent:
    /// ```plain
    ///     enter_span(Foo), string("text", None), exit_span()
    ///     string("text", Foo)
    /// ```
    /// The latter can be thought of as a shorthand for the former, which is
    /// more flexible.
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class)
                          -> io::Result<()>;
}

// Implement `Writer` for anthing that can be written to, this just implements
// the default rustdoc behaviour.
impl Writer for U {
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class)
                          -> io::Result<()> {
        match klass {
            Class::None => write!(self, "{}", text),
            klass => write!(self, "{}", klass.rustdoc_class(), text),
        }
    }

    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
        write!(self, "", klass.rustdoc_class())
    }

    fn exit_span(&mut self) -> io::Result<()> {
        write!(self, "")
    }
}

impl<'a> Classifier<'a> {
    fn new(lexer: lexer::StringReader<'a>, source_map: &'a SourceMap) -> Classifier<'a> {
        Classifier {
            lexer,
            source_map,
            in_attribute: false,
            in_macro: false,
            in_macro_nonterminal: false,
        }
    }

    /// Gets the next token out of the lexer, emitting fatal errors if lexing fails.
    fn try_next_token(&mut self) -> io::Result<TokenAndSpan> {
        match self.lexer.try_next_token() {
            Ok(tas) => Ok(tas),
            Err(_) => {
                let mut err = self.lexer.sess.span_diagnostic
                                  .struct_warn("Backing out of syntax highlighting");
                err.note("You probably did not intend to render this as a rust code-block");
                err.emit();
                Err(io::Error::new(io::ErrorKind::Other, ""))
            }
        }
    }

    /// Exhausts the `lexer` writing the output into `out`.
    ///
    /// The general structure for this method is to iterate over each token,
    /// possibly giving it an HTML span with a class specifying what flavor of token
    /// is used. All source code emission is done as slices from the source map,
    /// not from the tokens themselves, in order to stay true to the original
    /// source.
    fn write_source<W: Writer>(&mut self,
                                   out: &mut W)
                                   -> io::Result<()> {
        loop {
            let next = self.try_next_token()?;
            if next.tok == token::Eof {
                break;
            }

            self.write_token(out, next)?;
        }

        Ok(())
    }

    // Handles an individual token from the lexer.
    fn write_token<W: Writer>(&mut self,
                              out: &mut W,
                              tas: TokenAndSpan)
                              -> io::Result<()> {
        let klass = match tas.tok {
            token::Shebang(s) => {
                out.string(Escape(&s.as_str()), Class::None)?;
                return Ok(());
            },

            token::Whitespace => Class::None,
            token::Comment => Class::Comment,
            token::DocComment(..) => Class::DocComment,

            // If this '&' or '*' token is followed by a non-whitespace token, assume that it's the
            // reference or dereference operator or a reference or pointer type, instead of the
            // bit-and or multiplication operator.
            token::BinOp(token::And) | token::BinOp(token::Star)
                if self.lexer.peek().tok != token::Whitespace => Class::RefKeyWord,

            // Consider this as part of a macro invocation if there was a
            // leading identifier.
            token::Not if self.in_macro => {
                self.in_macro = false;
                Class::Macro
            }

            // Operators.
            token::Eq | token::Lt | token::Le | token::EqEq | token::Ne | token::Ge | token::Gt |
                token::AndAnd | token::OrOr | token::Not | token::BinOp(..) | token::RArrow |
                token::BinOpEq(..) | token::FatArrow => Class::Op,

            // Miscellaneous, no highlighting.
            token::Dot | token::DotDot | token::DotDotDot | token::DotDotEq | token::Comma |
                token::Semi | token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
                token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
                token::CloseDelim(token::NoDelim) => Class::None,

            token::Question => Class::QuestionMark,

            token::Dollar => {
                if self.lexer.peek().tok.is_ident() {
                    self.in_macro_nonterminal = true;
                    Class::MacroNonTerminal
                } else {
                    Class::None
                }
            }

            // This might be the start of an attribute. We're going to want to
            // continue highlighting it as an attribute until the ending ']' is
            // seen, so skip out early. Down below we terminate the attribute
            // span when we see the ']'.
            token::Pound => {
                // We can't be sure that our # begins an attribute (it could
                // just be appearing in a macro) until we read either `#![` or
                // `#[` from the input stream.
                //
                // We don't want to start highlighting as an attribute until
                // we're confident there is going to be a ] coming up, as
                // otherwise # tokens in macros highlight the rest of the input
                // as an attribute.

                // Case 1: #![inner_attribute]
                if self.lexer.peek().tok == token::Not {
                    self.try_next_token()?; // NOTE: consumes `!` token!
                    if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
                        self.in_attribute = true;
                        out.enter_span(Class::Attribute)?;
                    }
                    out.string("#", Class::None)?;
                    out.string("!", Class::None)?;
                    return Ok(());
                }

                // Case 2: #[outer_attribute]
                if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
                    self.in_attribute = true;
                    out.enter_span(Class::Attribute)?;
                }
                out.string("#", Class::None)?;
                return Ok(());
            }
            token::CloseDelim(token::Bracket) => {
                if self.in_attribute {
                    self.in_attribute = false;
                    out.string("]", Class::None)?;
                    out.exit_span()?;
                    return Ok(());
                } else {
                    Class::None
                }
            }

            token::Literal(lit, _suf) => {
                match lit {
                    // Text literals.
                    token::Byte(..) | token::Char(..) |
                        token::ByteStr(..) | token::ByteStrRaw(..) |
                        token::Str_(..) | token::StrRaw(..) => Class::String,

                    // Number literals.
                    token::Integer(..) | token::Float(..) => Class::Number,
                }
            }

            // Keywords are also included in the identifier set.
            token::Ident(ident, is_raw) => {
                match &*ident.as_str() {
                    "ref" | "mut" if !is_raw => Class::RefKeyWord,

                    "self" | "Self" => Class::Self_,
                    "false" | "true" if !is_raw => Class::Bool,

                    "Option" | "Result" => Class::PreludeTy,
                    "Some" | "None" | "Ok" | "Err" => Class::PreludeVal,

                    "$crate" => Class::KeyWord,
                    _ if tas.tok.is_reserved_ident() => Class::KeyWord,

                    _ => {
                        if self.in_macro_nonterminal {
                            self.in_macro_nonterminal = false;
                            Class::MacroNonTerminal
                        } else if self.lexer.peek().tok == token::Not {
                            self.in_macro = true;
                            Class::Macro
                        } else {
                            Class::Ident
                        }
                    }
                }
            }

            token::Lifetime(..) => Class::Lifetime,

            token::Eof | token::Interpolated(..) |
            token::Tilde | token::At| token::SingleQuote => Class::None,
        };

        // Anything that didn't return above is the simple case where we the
        // class just spans a single token, so we can use the `string` method.
        out.string(Escape(&self.snip(tas.sp)), klass)
    }

    // Helper function to get a snippet from the source_map.
    fn snip(&self, sp: Span) -> String {
        self.source_map.span_to_snippet(sp).unwrap()
    }
}

impl Class {
    /// Returns the css class expected by rustdoc for each `Class`.
    fn rustdoc_class(self) -> &'static str {
        match self {
            Class::None => "",
            Class::Comment => "comment",
            Class::DocComment => "doccomment",
            Class::Attribute => "attribute",
            Class::KeyWord => "kw",
            Class::RefKeyWord => "kw-2",
            Class::Self_ => "self",
            Class::Op => "op",
            Class::Macro => "macro",
            Class::MacroNonTerminal => "macro-nonterminal",
            Class::String => "string",
            Class::Number => "number",
            Class::Bool => "bool-val",
            Class::Ident => "ident",
            Class::Lifetime => "lifetime",
            Class::PreludeTy => "prelude-ty",
            Class::PreludeVal => "prelude-val",
            Class::QuestionMark => "question-mark"
        }
    }
}

fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> {
    write!(out, "

\n", class.unwrap_or(""))
}

fn write_footer(out: &mut dyn Write) -> io::Result<()> {
    write!(out, "

\n")
}
<commit_msg>Add token::Err<commit_after>//! Basic syntax highlighting functionality.
//!
//! This module uses libsyntax's lexer to provide token-based highlighting for
//! the HTML documentation generated by rustdoc.
//!
//! Use the `render_with_highlighting` to highlight some rust code.

use html::escape::Escape;

use std::fmt::Display;
use std::io;
use std::io::prelude::*;

use syntax::source_map::{SourceMap, FilePathMapping};
use syntax::parse::lexer::{self, TokenAndSpan};
use syntax::parse::token;
use syntax::parse;
use syntax_pos::{Span, FileName};

/// Highlights `src`, returning the HTML output.
pub fn render_with_highlighting(
    src: &str,
    class: Option<&str>,
    extension: Option<&str>,
    tooltip: Option<(&str, &str)>,
) -> String {
    debug!("highlighting: ================\n{}\n==============", src);
    let sess = parse::ParseSess::new(FilePathMapping::empty());
    let fm = sess.source_map().new_source_file(FileName::Custom("stdin".to_string()),
                                               src.to_string());

    let mut out = Vec::new();
    if let Some((tooltip, class)) = tooltip {
        write!(out, "

ⓘ{}

",
               class, tooltip).unwrap();
    }
    write_header(class, &mut out).unwrap();

    let lexer = match lexer::StringReader::new_without_err(&sess, fm, None, "Output from rustc:") {
        Ok(l) => l,
        Err(_) => {
            let first_line = src.lines().next().unwrap_or_else(|| "");
            let mut err = sess.span_diagnostic
                              .struct_warn(&format!("Invalid doc comment starting with: `{}`\n\
                                                     (Ignoring this codeblock)",
                                                    first_line));
            err.emit();
            return String::new();
        }
    };
    let mut classifier = Classifier::new(lexer, sess.source_map());
    if classifier.write_source(&mut out).is_err() {
        classifier.lexer.emit_fatal_errors();
        return format!("
{}
", src);
    }

    if let Some(extension) = extension {
        write!(out, "{}", extension).unwrap();
    }
    write_footer(&mut out).unwrap();
    String::from_utf8_lossy(&out[..]).into_owned()
}

/// Processes a program (nested in the internal `lexer`), classifying strings of
/// text by highlighting category (`Class`). Calls out to a `Writer` to write
/// each span of text in sequence.
struct Classifier<'a> {
    lexer: lexer::StringReader<'a>,
    source_map: &'a SourceMap,

    // State of the classifier.
    in_attribute: bool,
    in_macro: bool,
    in_macro_nonterminal: bool,
}

/// How a span of text is classified. Mostly corresponds to token kinds.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Class {
    None,
    Comment,
    DocComment,
    Attribute,
    KeyWord,
    // Keywords that do pointer/reference stuff.
    RefKeyWord,
    Self_,
    Op,
    Macro,
    MacroNonTerminal,
    String,
    Number,
    Bool,
    Ident,
    Lifetime,
    PreludeTy,
    PreludeVal,
    QuestionMark,
}

/// Trait that controls writing the output of syntax highlighting. Users should
/// implement this trait to customize writing output.
///
/// The classifier will call into the `Writer` implementation as it finds spans
/// of text to highlight. Exactly how that text should be highlighted is up to
/// the implementation.
trait Writer {
    /// Called when we start processing a span of text that should be highlighted.
    /// The `Class` argument specifies how it should be highlighted.
    fn enter_span(&mut self, _: Class) -> io::Result<()>;

    /// Called at the end of a span of highlighted text.
    fn exit_span(&mut self) -> io::Result<()>;

    /// Called for a span of text.  If the text should be highlighted differently from the
    /// surrounding text, then the `Class` argument will be a value other than `None`.
    ///
    /// The following sequences of callbacks are equivalent:
    /// ```plain
    ///     enter_span(Foo), string("text", None), exit_span()
    ///     string("text", Foo)
    /// ```
    /// The latter can be thought of as a shorthand for the former, which is
    /// more flexible.
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class)
                          -> io::Result<()>;
}

// Implement `Writer` for anthing that can be written to, this just implements
// the default rustdoc behaviour.
impl Writer for U {
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class)
                          -> io::Result<()> {
        match klass {
            Class::None => write!(self, "{}", text),
            klass => write!(self, "{}", klass.rustdoc_class(), text),
        }
    }

    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
        write!(self, "", klass.rustdoc_class())
    }

    fn exit_span(&mut self) -> io::Result<()> {
        write!(self, "")
    }
}

impl<'a> Classifier<'a> {
    fn new(lexer: lexer::StringReader<'a>, source_map: &'a SourceMap) -> Classifier<'a> {
        Classifier {
            lexer,
            source_map,
            in_attribute: false,
            in_macro: false,
            in_macro_nonterminal: false,
        }
    }

    /// Gets the next token out of the lexer, emitting fatal errors if lexing fails.
    fn try_next_token(&mut self) -> io::Result<TokenAndSpan> {
        match self.lexer.try_next_token() {
            Ok(tas) => Ok(tas),
            Err(_) => {
                let mut err = self.lexer.sess.span_diagnostic
                                  .struct_warn("Backing out of syntax highlighting");
                err.note("You probably did not intend to render this as a rust code-block");
                err.emit();
                Err(io::Error::new(io::ErrorKind::Other, ""))
            }
        }
    }

    /// Exhausts the `lexer` writing the output into `out`.
    ///
    /// The general structure for this method is to iterate over each token,
    /// possibly giving it an HTML span with a class specifying what flavor of token
    /// is used. All source code emission is done as slices from the source map,
    /// not from the tokens themselves, in order to stay true to the original
    /// source.
    fn write_source<W: Writer>(&mut self,
                                   out: &mut W)
                                   -> io::Result<()> {
        loop {
            let next = self.try_next_token()?;
            if next.tok == token::Eof {
                break;
            }

            self.write_token(out, next)?;
        }

        Ok(())
    }

    // Handles an individual token from the lexer.
    fn write_token<W: Writer>(&mut self,
                              out: &mut W,
                              tas: TokenAndSpan)
                              -> io::Result<()> {
        let klass = match tas.tok {
            token::Shebang(s) => {
                out.string(Escape(&s.as_str()), Class::None)?;
                return Ok(());
            },

            token::Whitespace => Class::None,
            token::Comment => Class::Comment,
            token::DocComment(..) => Class::DocComment,

            // If this '&' or '*' token is followed by a non-whitespace token, assume that it's the
            // reference or dereference operator or a reference or pointer type, instead of the
            // bit-and or multiplication operator.
            token::BinOp(token::And) | token::BinOp(token::Star)
                if self.lexer.peek().tok != token::Whitespace => Class::RefKeyWord,

            // Consider this as part of a macro invocation if there was a
            // leading identifier.
            token::Not if self.in_macro => {
                self.in_macro = false;
                Class::Macro
            }

            // Operators.
            token::Eq | token::Lt | token::Le | token::EqEq | token::Ne | token::Ge | token::Gt |
                token::AndAnd | token::OrOr | token::Not | token::BinOp(..) | token::RArrow |
                token::BinOpEq(..) | token::FatArrow => Class::Op,

            // Miscellaneous, no highlighting.
            token::Dot | token::DotDot | token::DotDotDot | token::DotDotEq | token::Comma |
                token::Semi | token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
                token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
                token::CloseDelim(token::NoDelim) => Class::None,

            token::Question => Class::QuestionMark,

            token::Dollar => {
                if self.lexer.peek().tok.is_ident() {
                    self.in_macro_nonterminal = true;
                    Class::MacroNonTerminal
                } else {
                    Class::None
                }
            }

            // This might be the start of an attribute. We're going to want to
            // continue highlighting it as an attribute until the ending ']' is
            // seen, so skip out early. Down below we terminate the attribute
            // span when we see the ']'.
            token::Pound => {
                // We can't be sure that our # begins an attribute (it could
                // just be appearing in a macro) until we read either `#![` or
                // `#[` from the input stream.
                //
                // We don't want to start highlighting as an attribute until
                // we're confident there is going to be a ] coming up, as
                // otherwise # tokens in macros highlight the rest of the input
                // as an attribute.

                // Case 1: #![inner_attribute]
                if self.lexer.peek().tok == token::Not {
                    self.try_next_token()?; // NOTE: consumes `!` token!
                    if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
                        self.in_attribute = true;
                        out.enter_span(Class::Attribute)?;
                    }
                    out.string("#", Class::None)?;
                    out.string("!", Class::None)?;
                    return Ok(());
                }

                // Case 2: #[outer_attribute]
                if self.lexer.peek().tok == token::OpenDelim(token::Bracket) {
                    self.in_attribute = true;
                    out.enter_span(Class::Attribute)?;
                }
                out.string("#", Class::None)?;
                return Ok(());
            }
            token::CloseDelim(token::Bracket) => {
                if self.in_attribute {
                    self.in_attribute = false;
                    out.string("]", Class::None)?;
                    out.exit_span()?;
                    return Ok(());
                } else {
                    Class::None
                }
            }

            token::Literal(lit, _suf) => {
                match lit {
                    // Text literals.
                    token::Byte(..) | token::Char(..) | token::Err(..) |
                        token::ByteStr(..) | token::ByteStrRaw(..) |
                        token::Str_(..) | token::StrRaw(..) => Class::String,

                    // Number literals.
                    token::Integer(..) | token::Float(..) => Class::Number,
                }
            }

            // Keywords are also included in the identifier set.
            token::Ident(ident, is_raw) => {
                match &*ident.as_str() {
                    "ref" | "mut" if !is_raw => Class::RefKeyWord,

                    "self" | "Self" => Class::Self_,
                    "false" | "true" if !is_raw => Class::Bool,

                    "Option" | "Result" => Class::PreludeTy,
                    "Some" | "None" | "Ok" | "Err" => Class::PreludeVal,

                    "$crate" => Class::KeyWord,
                    _ if tas.tok.is_reserved_ident() => Class::KeyWord,

                    _ => {
                        if self.in_macro_nonterminal {
                            self.in_macro_nonterminal = false;
                            Class::MacroNonTerminal
                        } else if self.lexer.peek().tok == token::Not {
                            self.in_macro = true;
                            Class::Macro
                        } else {
                            Class::Ident
                        }
                    }
                }
            }

            token::Lifetime(..) => Class::Lifetime,

            token::Eof | token::Interpolated(..) |
            token::Tilde | token::At| token::SingleQuote => Class::None,
        };

        // Anything that didn't return above is the simple case where we the
        // class just spans a single token, so we can use the `string` method.
        out.string(Escape(&self.snip(tas.sp)), klass)
    }

    // Helper function to get a snippet from the source_map.
    fn snip(&self, sp: Span) -> String {
        self.source_map.span_to_snippet(sp).unwrap()
    }
}

impl Class {
    /// Returns the css class expected by rustdoc for each `Class`.
    fn rustdoc_class(self) -> &'static str {
        match self {
            Class::None => "",
            Class::Comment => "comment",
            Class::DocComment => "doccomment",
            Class::Attribute => "attribute",
            Class::KeyWord => "kw",
            Class::RefKeyWord => "kw-2",
            Class::Self_ => "self",
            Class::Op => "op",
            Class::Macro => "macro",
            Class::MacroNonTerminal => "macro-nonterminal",
            Class::String => "string",
            Class::Number => "number",
            Class::Bool => "bool-val",
            Class::Ident => "ident",
            Class::Lifetime => "lifetime",
            Class::PreludeTy => "prelude-ty",
            Class::PreludeVal => "prelude-val",
            Class::QuestionMark => "question-mark"
        }
    }
}

fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> {
    write!(out, "

\n", class.unwrap_or(""))
}

fn write_footer(out: &mut dyn Write) -> io::Result<()> {
    write!(out, "

\n")
}
<|endoftext|>