<commit_before>#![allow(dead_code, unused_imports)]

extern crate bytecount;
#[macro_use]
extern crate clap;
extern crate ctrlc;
extern crate env_logger;
extern crate grep;
extern crate ignore;
#[cfg(windows)]
extern crate kernel32;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate memmap;
extern crate num_cpus;
extern crate regex;
extern crate termcolor;
#[cfg(windows)]
extern crate winapi;

use std::error::Error;
use std::io;
use std::io::Write;
use std::process;
use std::result;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc;
use std::thread;

use termcolor::WriteColor;

use args::Args;
use worker::Work;

macro_rules! errored {
    ($($tt:tt)*) => {
        return Err(From::from(format!($($tt)*)));
    }
}

macro_rules! eprintln {
    ($($tt:tt)*) => {{
        use std::io::Write;
        let _ = writeln!(&mut ::std::io::stderr(), $($tt)*);
    }}
}

mod app;
mod args;
mod atty;
mod pathutil;
mod printer;
mod search_buffer;
mod search_stream;
mod unescape;
mod worker;

pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;

fn main() {
    match Args::parse().map(Arc::new).and_then(run) {
        Ok(count) if count == 0 => process::exit(1),
        Ok(_) => process::exit(0),
        Err(err) => {
            eprintln!("{}", err);
            process::exit(1);
        }
    }
}

fn run(args: Arc<Args>) -> Result<u64> {
    if args.never_match() {
        return Ok(0);
    }
    {
        let args = args.clone();
        ctrlc::set_handler(move || {
            let mut writer = args.stdout();
            let _ = writer.reset();
            let _ = writer.flush();
            process::exit(1);
        });
    }
    let threads = args.threads();
    if args.files() {
        if threads == 1 || args.is_one_path() {
            run_files_one_thread(args)
        } else {
            run_files_parallel(args)
        }
    } else if args.type_list() {
        run_types(args)
    } else if threads == 1 || args.is_one_path() {
        run_one_thread(args)
    } else {
        run_parallel(args)
    }
}

fn run_parallel(args: Arc<Args>) -> Result<u64> {
    let bufwtr = Arc::new(args.buffer_writer());
    let quiet_matched = QuietMatched::new(args.quiet());
    let paths_searched = Arc::new(AtomicUsize::new(0));
    let match_count = Arc::new(AtomicUsize::new(0));

    args.walker_parallel().run(|| {
        let args = args.clone();
        let quiet_matched = quiet_matched.clone();
        let paths_searched = paths_searched.clone();
        let match_count = match_count.clone();
        let bufwtr = bufwtr.clone();
        let mut buf = bufwtr.buffer();
        let mut worker = args.worker();
        Box::new(move |result| {
            use ignore::WalkState::*;

            if quiet_matched.has_match() {
                return Quit;
            }
            let dent = match get_or_log_dir_entry(result, args.no_messages()) {
                None => return Continue,
                Some(dent) => dent,
            };
            paths_searched.fetch_add(1, Ordering::SeqCst);
            buf.clear();
            {
                // This block actually executes the search and prints the
                // results into outbuf.
                let mut printer = args.printer(&mut buf);
                let count =
                    if dent.is_stdin() {
                        worker.run(&mut printer, Work::Stdin)
                    } else {
                        worker.run(&mut printer, Work::DirEntry(dent))
                    };
                match_count.fetch_add(count as usize, Ordering::SeqCst);
                if quiet_matched.set_match(count > 0) {
                    return Quit;
                }
            }
            // BUG(burntsushi): We should handle this error instead of ignoring
            // it. See: https://github.com/BurntSushi/ripgrep/issues/200
            let _ = bufwtr.print(&buf);
            Continue
        })
    });
    if !args.paths().is_empty() && paths_searched.load(Ordering::SeqCst) == 0 {
        if !args.no_messages() {
            eprint_nothing_searched();
        }
    }
    Ok(match_count.load(Ordering::SeqCst) as u64)
}

fn run_one_thread(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut stdout = stdout.lock();
    let mut worker = args.worker();
    let mut paths_searched: u64 = 0;
    let mut match_count = 0;
    for result in args.walker() {
        let dent = match get_or_log_dir_entry(result, args.no_messages()) {
            None => continue,
            Some(dent) => dent,
        };
        let mut printer = args.printer(&mut stdout);
        if match_count > 0 {
            if args.quiet() {
                break;
            }
            if let Some(sep) = args.file_separator() {
                printer = printer.file_separator(sep);
            }
        }
        paths_searched += 1;
        match_count +=
            if dent.is_stdin() {
                worker.run(&mut printer, Work::Stdin)
            } else {
                worker.run(&mut printer, Work::DirEntry(dent))
            };
    }
    if !args.paths().is_empty() && paths_searched == 0 {
        if !args.no_messages() {
            eprint_nothing_searched();
        }
    }
    Ok(match_count)
}

fn run_files_parallel(args: Arc<Args>) -> Result<u64> {
    let print_args = args.clone();
    let (tx, rx) = mpsc::channel::<ignore::DirEntry>();
    let print_thread = thread::spawn(move || {
        let stdout = print_args.stdout();
        let mut printer = print_args.printer(stdout.lock());
        let mut file_count = 0;
        for dent in rx.iter() {
            printer.path(dent.path());
            file_count += 1;
        }
        file_count
    });
    let no_messages = args.no_messages();
    args.walker_parallel().run(move || {
        let tx = tx.clone();
        Box::new(move |result| {
            if let Some(dent) = get_or_log_dir_entry(result, no_messages) {
                tx.send(dent).unwrap();
            }
            ignore::WalkState::Continue
        })
    });
    Ok(print_thread.join().unwrap())
}

fn run_files_one_thread(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut printer = args.printer(stdout.lock());
    let mut file_count = 0;
    for result in args.walker() {
        let dent = match get_or_log_dir_entry(result, args.no_messages()) {
            None => continue,
            Some(dent) => dent,
        };
        printer.path(dent.path());
        file_count += 1;
    }
    Ok(file_count)
}

fn run_types(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut printer = args.printer(stdout.lock());
    let mut ty_count = 0;
    for def in args.type_defs() {
        printer.type_def(def);
        ty_count += 1;
    }
    Ok(ty_count)
}

fn get_or_log_dir_entry(
    result: result::Result<ignore::DirEntry, ignore::Error>,
    no_messages: bool,
) -> Option<ignore::DirEntry> {
    match result {
        Err(err) => {
            if !no_messages {
                eprintln!("{}", err);
            }
            None
        }
        Ok(dent) => {
            if let Some(err) = dent.error() {
                if !no_messages {
                    eprintln!("{}", err);
                }
            }
            let ft = match dent.file_type() {
                None => return Some(dent), // entry is stdin
                Some(ft) => ft,
            };
            // A depth of 0 means the user gave the path explicitly, so we
            // should always try to search it.
            if dent.depth() == 0 && !ft.is_dir() {
                Some(dent)
            } else if ft.is_file() {
                Some(dent)
            } else {
                None
            }
        }
    }
}

fn version() -> String {
    let (maj, min, pat) = (
        option_env!("CARGO_PKG_VERSION_MAJOR"),
        option_env!("CARGO_PKG_VERSION_MINOR"),
        option_env!("CARGO_PKG_VERSION_PATCH"),
    );
    match (maj, min, pat) {
        (Some(maj), Some(min), Some(pat)) => {
            format!("ripgrep {}.{}.{}", maj, min, pat)
        }
        _ => "".to_owned(),
    }
}

fn eprint_nothing_searched() {
    eprintln!("No files were searched, which means ripgrep probably \
               applied a filter you didn't expect. \
               Try running again with --debug.");
}

/// A simple thread safe abstraction for determining whether a search should
/// stop if the user has requested quiet mode.
#[derive(Clone, Debug)]
pub struct QuietMatched(Arc<Option<AtomicBool>>);

impl QuietMatched {
    /// Create a new QuietMatched value.
    ///
    /// If quiet is true, then set_match and has_match will reflect whether
    /// a search should quit or not because it found a match.
    ///
    /// If quiet is false, then set_match is always a no-op and has_match
    /// always returns false.
    pub fn new(quiet: bool) -> QuietMatched {
        let atomic = if quiet { Some(AtomicBool::new(false)) } else { None };
        QuietMatched(Arc::new(atomic))
    }

    /// Returns true if and only if quiet mode is enabled and a match has
    /// occurred.
    pub fn has_match(&self) -> bool {
        match *self.0 {
            None => false,
            Some(ref matched) => matched.load(Ordering::SeqCst),
        }
    }

    /// Sets whether a match has occurred or not.
    ///
    /// If quiet mode is disabled, then this is a no-op.
    pub fn set_match(&self, yes: bool) -> bool {
        match *self.0 {
            None => false,
            Some(_) if !yes => false,
            Some(ref m) => { m.store(true, Ordering::SeqCst); true }
        }
    }
}
<commit_msg>Simplify code.<commit_after>#![allow(dead_code, unused_imports)]

extern crate bytecount;
#[macro_use]
extern crate clap;
extern crate ctrlc;
extern crate env_logger;
extern crate grep;
extern crate ignore;
#[cfg(windows)]
extern crate kernel32;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate memmap;
extern crate num_cpus;
extern crate regex;
extern crate termcolor;
#[cfg(windows)]
extern crate winapi;

use std::error::Error;
use std::io;
use std::io::Write;
use std::process;
use std::result;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc;
use std::thread;

use termcolor::WriteColor;

use args::Args;
use worker::Work;

macro_rules! errored {
    ($($tt:tt)*) => {
        return Err(From::from(format!($($tt)*)));
    }
}

macro_rules! eprintln {
    ($($tt:tt)*) => {{
        use std::io::Write;
        let _ = writeln!(&mut ::std::io::stderr(), $($tt)*);
    }}
}

mod app;
mod args;
mod atty;
mod pathutil;
mod printer;
mod search_buffer;
mod search_stream;
mod unescape;
mod worker;

pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;

fn main() {
    match Args::parse().map(Arc::new).and_then(run) {
        Ok(0) => process::exit(1),
        Ok(_) => process::exit(0),
        Err(err) => {
            eprintln!("{}", err);
            process::exit(1);
        }
    }
}

fn run(args: Arc<Args>) -> Result<u64> {
    if args.never_match() {
        return Ok(0);
    }
    {
        let args = args.clone();
        ctrlc::set_handler(move || {
            let mut writer = args.stdout();
            let _ = writer.reset();
            let _ = writer.flush();
            process::exit(1);
        });
    }
    let threads = args.threads();
    if args.files() {
        if threads == 1 || args.is_one_path() {
            run_files_one_thread(args)
        } else {
            run_files_parallel(args)
        }
    } else if args.type_list() {
        run_types(args)
    } else if threads == 1 || args.is_one_path() {
        run_one_thread(args)
    } else {
        run_parallel(args)
    }
}

fn run_parallel(args: Arc<Args>) -> Result<u64> {
    let bufwtr = Arc::new(args.buffer_writer());
    let quiet_matched = QuietMatched::new(args.quiet());
    let paths_searched = Arc::new(AtomicUsize::new(0));
    let match_count = Arc::new(AtomicUsize::new(0));

    args.walker_parallel().run(|| {
        let args = args.clone();
        let quiet_matched = quiet_matched.clone();
        let paths_searched = paths_searched.clone();
        let match_count = match_count.clone();
        let bufwtr = bufwtr.clone();
        let mut buf = bufwtr.buffer();
        let mut worker = args.worker();
        Box::new(move |result| {
            use ignore::WalkState::*;

            if quiet_matched.has_match() {
                return Quit;
            }
            let dent = match get_or_log_dir_entry(result, args.no_messages()) {
                None => return Continue,
                Some(dent) => dent,
            };
            paths_searched.fetch_add(1, Ordering::SeqCst);
            buf.clear();
            {
                // This block actually executes the search and prints the
                // results into outbuf.
                let mut printer = args.printer(&mut buf);
                let count =
                    if dent.is_stdin() {
                        worker.run(&mut printer, Work::Stdin)
                    } else {
                        worker.run(&mut printer, Work::DirEntry(dent))
                    };
                match_count.fetch_add(count as usize, Ordering::SeqCst);
                if quiet_matched.set_match(count > 0) {
                    return Quit;
                }
            }
            // BUG(burntsushi): We should handle this error instead of ignoring
            // it. See: https://github.com/BurntSushi/ripgrep/issues/200
            let _ = bufwtr.print(&buf);
            Continue
        })
    });
    if !args.paths().is_empty() && paths_searched.load(Ordering::SeqCst) == 0 {
        if !args.no_messages() {
            eprint_nothing_searched();
        }
    }
    Ok(match_count.load(Ordering::SeqCst) as u64)
}

fn run_one_thread(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut stdout = stdout.lock();
    let mut worker = args.worker();
    let mut paths_searched: u64 = 0;
    let mut match_count = 0;
    for result in args.walker() {
        let dent = match get_or_log_dir_entry(result, args.no_messages()) {
            None => continue,
            Some(dent) => dent,
        };
        let mut printer = args.printer(&mut stdout);
        if match_count > 0 {
            if args.quiet() {
                break;
            }
            if let Some(sep) = args.file_separator() {
                printer = printer.file_separator(sep);
            }
        }
        paths_searched += 1;
        match_count +=
            if dent.is_stdin() {
                worker.run(&mut printer, Work::Stdin)
            } else {
                worker.run(&mut printer, Work::DirEntry(dent))
            };
    }
    if !args.paths().is_empty() && paths_searched == 0 {
        if !args.no_messages() {
            eprint_nothing_searched();
        }
    }
    Ok(match_count)
}

fn run_files_parallel(args: Arc<Args>) -> Result<u64> {
    let print_args = args.clone();
    let (tx, rx) = mpsc::channel::<ignore::DirEntry>();
    let print_thread = thread::spawn(move || {
        let stdout = print_args.stdout();
        let mut printer = print_args.printer(stdout.lock());
        let mut file_count = 0;
        for dent in rx.iter() {
            printer.path(dent.path());
            file_count += 1;
        }
        file_count
    });
    let no_messages = args.no_messages();
    args.walker_parallel().run(move || {
        let tx = tx.clone();
        Box::new(move |result| {
            if let Some(dent) = get_or_log_dir_entry(result, no_messages) {
                tx.send(dent).unwrap();
            }
            ignore::WalkState::Continue
        })
    });
    Ok(print_thread.join().unwrap())
}

fn run_files_one_thread(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut printer = args.printer(stdout.lock());
    let mut file_count = 0;
    for result in args.walker() {
        let dent = match get_or_log_dir_entry(result, args.no_messages()) {
            None => continue,
            Some(dent) => dent,
        };
        printer.path(dent.path());
        file_count += 1;
    }
    Ok(file_count)
}

fn run_types(args: Arc<Args>) -> Result<u64> {
    let stdout = args.stdout();
    let mut printer = args.printer(stdout.lock());
    let mut ty_count = 0;
    for def in args.type_defs() {
        printer.type_def(def);
        ty_count += 1;
    }
    Ok(ty_count)
}

fn get_or_log_dir_entry(
    result: result::Result<ignore::DirEntry, ignore::Error>,
    no_messages: bool,
) -> Option<ignore::DirEntry> {
    match result {
        Err(err) => {
            if !no_messages {
                eprintln!("{}", err);
            }
            None
        }
        Ok(dent) => {
            if let Some(err) = dent.error() {
                if !no_messages {
                    eprintln!("{}", err);
                }
            }
            let ft = match dent.file_type() {
                None => return Some(dent), // entry is stdin
                Some(ft) => ft,
            };
            // A depth of 0 means the user gave the path explicitly, so we
            // should always try to search it.
            if dent.depth() == 0 && !ft.is_dir() {
                Some(dent)
            } else if ft.is_file() {
                Some(dent)
            } else {
                None
            }
        }
    }
}

fn version() -> String {
    let (maj, min, pat) = (
        option_env!("CARGO_PKG_VERSION_MAJOR"),
        option_env!("CARGO_PKG_VERSION_MINOR"),
        option_env!("CARGO_PKG_VERSION_PATCH"),
    );
    match (maj, min, pat) {
        (Some(maj), Some(min), Some(pat)) => {
            format!("ripgrep {}.{}.{}", maj, min, pat)
        }
        _ => "".to_owned(),
    }
}

fn eprint_nothing_searched() {
    eprintln!("No files were searched, which means ripgrep probably \
               applied a filter you didn't expect. \
               Try running again with --debug.");
}

/// A simple thread safe abstraction for determining whether a search should
/// stop if the user has requested quiet mode.
#[derive(Clone, Debug)]
pub struct QuietMatched(Arc<Option<AtomicBool>>);

impl QuietMatched {
    /// Create a new QuietMatched value.
    ///
    /// If quiet is true, then set_match and has_match will reflect whether
    /// a search should quit or not because it found a match.
    ///
    /// If quiet is false, then set_match is always a no-op and has_match
    /// always returns false.
    pub fn new(quiet: bool) -> QuietMatched {
        let atomic = if quiet { Some(AtomicBool::new(false)) } else { None };
        QuietMatched(Arc::new(atomic))
    }

    /// Returns true if and only if quiet mode is enabled and a match has
    /// occurred.
    pub fn has_match(&self) -> bool {
        match *self.0 {
            None => false,
            Some(ref matched) => matched.load(Ordering::SeqCst),
        }
    }

    /// Sets whether a match has occurred or not.
    ///
    /// If quiet mode is disabled, then this is a no-op.
    pub fn set_match(&self, yes: bool) -> bool {
        match *self.0 {
            None => false,
            Some(_) if !yes => false,
            Some(ref m) => { m.store(true, Ordering::SeqCst); true }
        }
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(globs, macro_rules, asm, phase, unsafe_destructor, lang_items)]
#![crate_name="zinc"]
#![crate_type="rlib"]
#![allow(ctypes)]
#![no_std]

/*!
Zinc is an embedded stack for rust.

Zinc provides a complete embedded stack for application development in rust. It
is provided in a form of library, compiled for a specific MCU, that can be
linked into user's own applications.

### Supported architectures

ARM is the only architecture, supported at the moment. Zinc can be compiled for
"native" architecture as well, which should be useful only for testing the code,
though.

### Supported ARM MCUs

Two MCUs are supported at the moment, specifically

 * NXP LPC1768
 * ST STM32F407

The code is generic enough to support other MCUs in the same family (LPC17xx and
STM32F403/407).
*/

extern crate core;

#[cfg(test)] #[phase(plugin,link)] extern crate std;
#[cfg(test)] extern crate native;

pub mod drivers;
pub mod hal;
pub mod lib;
pub mod os;

// TODO(farcaller): clean up when fixed.
#[cfg(not(test))]
mod std {
  pub use core::cmp;  // used for #[deriving(Eq)] until fixed in rust.
}

#[test]
fn dummy_test() {
}
<commit_msg>Remove dummy test<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(globs, macro_rules, asm, phase, unsafe_destructor, lang_items)]
#![crate_name="zinc"]
#![crate_type="rlib"]
#![allow(ctypes)]
#![no_std]

/*!
Zinc is an embedded stack for rust.

Zinc provides a complete embedded stack for application development in rust. It
is provided in a form of library, compiled for a specific MCU, that can be
linked into user's own applications.

### Supported architectures

ARM is the only architecture, supported at the moment. Zinc can be compiled for
"native" architecture as well, which should be useful only for testing the code,
though.

### Supported ARM MCUs

Two MCUs are supported at the moment, specifically

 * NXP LPC1768
 * ST STM32F407

The code is generic enough to support other MCUs in the same family (LPC17xx and
STM32F403/407).
*/

extern crate core;

#[cfg(test)] #[phase(plugin,link)] extern crate std;
#[cfg(test)] extern crate native;

pub mod drivers;
pub mod hal;
pub mod lib;
pub mod os;

// TODO(farcaller): clean up when fixed.
#[cfg(not(test))]
mod std {
  pub use core::cmp;  // used for #[deriving(Eq)] until fixed in rust.
}
<|endoftext|>


<commit_before>extern crate combine;
extern crate nix; // As in *nix
extern crate rand;
extern crate signal;

mod commander;

// Reference: https://github.com/NixOS/nixpkgs/blob/b07051ce6c3ba3039c89b6755da279002b0c3ace/nixos/modules/virtualisation/nixos-container.pl

use nix::sys::signal::{kill, SIGTERM, SIGINT};
use std::io;
use std::io::Write;
use std::process::Command;

#[derive(Debug)]
enum Error {
    Usage(String),
    NonZero(String, i32),
    ControlError(String),
    IoError(io::Error),
    NixError(nix::Error),
    Interrupted(commander::Signal)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

impl From<nix::Error> for Error {
    fn from(e: nix::Error) -> Self { NixError(e) }
}

impl From<commander::NonZero> for Error {
    fn from(e: commander::NonZero) -> Self { match e { commander::NonZero::NonZero(s, o) => NonZero(s, o), commander::NonZero::Interrupted(n) => Interrupted(n) } }
}

type Result<T> = std::result::Result<T, Error>;

// TODO: why can't this be inferred away
fn run<T>(cmd: &mut Command) -> Result<T> where T: commander::CommandOut { commander::run(cmd) }

fn probably_unique_name(length: usize) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

// Result is of the form "10.233.x"
fn unused_ip_block() -> Result<String> {
    // Can bail early anywhere here because we've got the lock
    let mut used = std::collections::HashSet::new();
    for entry in try!(std::fs::read_dir("/etc/containers")) {
        let entry = try!(entry);
        if try!(entry.file_type()).is_file() {
            let mut str = String::new();
            use std::io::Read;
            try!(try!(std::fs::File::open(entry.path())).read_to_string(&mut str));
            for line in str.lines() {
                use combine::{string, many1, choice, digit, token, eof, Parser, ParserExt};
                let ip_component = || { string("10.233.").with(many1::<String, _>(digit())).skip(token('.')).skip(many1::<String, _>(digit())) };
                if let Ok((c, _)) = choice([string("HOST_ADDRESS="), string("LOCAL_ADDRESS=")]).with(ip_component()).skip(eof()).parse(line) {
                    used.insert(c);
                }
            }
        }
    }
    (0..255).map(|p| p.to_string()).find(|s| !used.contains(s)).map(|s| format!("10.233.{}", s)).ok_or(ControlError("Out of IP addresses".to_string()))
}

fn mkpath(mode : u32, path : &str) -> Result<()> {
    use std::os::unix::fs::DirBuilderExt;
    match std::fs::DirBuilder::new().mode(mode).create(path) {
        Err(e) => if let std::io::ErrorKind::AlreadyExists = e.kind() { Ok(()) } else { Err(IoError(e)) },
        Ok(()) => Ok(())
    }
}

fn system_init() -> Result<()> {
    try!(mkpath(0o0755, "/etc/containers"));
    try!(mkpath(0o0700, "/var/lib/containers"));
    Ok(())
}

fn profile_dir(name : &str) -> String {
    format!("/nix/var/nix/profiles/per-container/{}", name)
}

fn container_root(name : &str) -> String {
    format!("/var/lib/containers/{}", name)
}

fn conf_file(name : &str) -> String {
    format!("/etc/containers/{}.conf", name)
}

fn create(config_file : &str) -> Result<String> {
    fn init_container_unsynced(name : &str) -> Result<()> {
        let ip_block = try!(unused_ip_block());
        // Use create_new to avoid clobbering existing container
        let mut f = try!(std::fs::OpenOptions::new().create_new(true).write(true).open(conf_file(name)));
        write!(&mut f, "PRIVATE_NETWORK=1\nHOST_ADDRESS={0}.1\nLOCAL_ADDRESS={0}.2\nAUTO_START=0\n", ip_block).map_err(IoError)
    }

    fn populate_container(name : &str, config_file : &str) -> Result<()> {
        use std::os::unix::fs::DirBuilderExt;
        // The per-container directory is restricted to prevent users on
        // the host from messing with guest users who happen to have the
        // same uid.
        try!(mkpath(0o0700, "/nix/var/nix/profiles/per-container"));
        let profile_dir = profile_dir(name);
        let mut dir_builder = std::fs::DirBuilder::new();
        try!(dir_builder.mode(0o0755).create(&profile_dir));

        let container_root = container_root(name);
        dir_builder.recursive(true);
        try!(dir_builder.mode(0o0755).create(format!("{}/etc/nixos", container_root)));

        // Write configuration.nix into the container fs
        let configuration_nix = format!("{}/etc/nixos/configuration.nix", container_root);
        {
            let mut f = try!(std::fs::File::create(format!("{}/etc/nixos/configuration.nix", container_root)));
            try!(write!(&mut f, "
    {{ config, lib, pkgs, ... }}:
    with lib;

    {{
        boot.isContainer = true;
        networking.hostName = mkDefault \"{}\";
        networking.useDHCP = false;
        imports = [ {} ];
    }}", name, config_file));
            // TODO: guaranteed sync at end of block?
        }

        // Build the container config
        run(Command::new("nix-env")
            .arg("-p").arg(format!("{}/system", profile_dir))
            .arg("-I").arg(format!("nixos-config={}", configuration_nix))
            .arg("-f").arg("<nixpkgs/nixos>")
            .arg("--set")
            .arg("-A").arg("system")
            .arg("--show-trace"))
    }

    let name = format!("fr{}", probably_unique_name(9));

    // Acquire lock
    use std::os::unix::io::IntoRawFd;
    let lock_fd = try!(std::fs::OpenOptions::new().create(true).append(true).open("/run/lock/nixos-container")).into_raw_fd();
    try!(nix::fcntl::flock(lock_fd, nix::fcntl::FlockArg::LockExclusive));

    let res = init_container_unsynced(&name);

    // Drop lock
    unsafe {
        let _ = nix::libc::close(lock_fd);  // If closing fails, ¯\_(ツ)_/¯
    }

    match res.and_then(|()| populate_container(&name, config_file)) {
        Ok(()) => Ok(name),
        // Don't leave a half-baked container behind
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn run_test(container_name : &str, args : &[String]) -> Result<i32> {
    try!(run(Command::new("systemctl").arg("start").arg(format!("container@{}", container_name))));
    let leader_pid = try!(run(Command::new("machinectl").arg("show").arg(container_name).arg("-p").arg("Leader")).and_then(|o : String| {
        use combine::{string, many1, digit, spaces, eof, Parser, ParserExt};
        string("Leader=").with(many1::<String, _>(digit())).skip(spaces()).skip(eof()).parse(o.as_str())
            .map(|(ds, _)| ds)
            .map_err(|_| ControlError(format!("Bad machinectl output {}", o)))
    }));
    let su_cmd = args.iter().map(|s| format!("'{}'", s.replace("'", "'\\''"))).collect::<Vec<String>>().join(" ");
    match run(Command::new("nsenter")
            .arg("-t").arg(leader_pid)
            .arg("-m").arg("-u").arg("-i").arg("-n").arg("-p")
            .arg("--").arg("/var/setuid-wrappers/su").arg("root").arg("-l").arg("-c").arg(format!("exec {}", su_cmd))) {
        Ok(()) => Ok(0),
        Err(NonZero(_, code)) => Ok(code),
        Err(e) => Err(e)
    }
}

// Remove a directory while recursively unmounting all mounted filesystems within
// that directory and unmounting/removing that directory afterwards as well.
//
// Specified path shouldn't be a mountpoint.
fn safe_remove_tree(path : &str) -> Result<()> {
    if !std::path::Path::new(path).is_dir() {
        return Ok(());
    }
    try!(run(Command::new("find").arg(path)
        .arg("-mindepth").arg("1")
        .arg("-xdev")
        .arg("(")
            .arg("-type").arg("d")
            .arg("-exec").arg("mountpoint").arg("-q").arg("{}").arg(";")
        .arg(")")
        .arg("-exec").arg("umount").arg("-fR").arg("{}").arg("+")));
    try!(run(Command::new("rm").arg("--one-file-system").arg("-rf").arg(path)));

    Ok(())
}

fn stop(container_name : &str) -> Result<()> {
    match run(Command::new("systemctl").arg("stop").arg(format!("container@{}", container_name))) {
        Ok(()) => Ok(()),
        Err(Interrupted(_)) => {
            return run(Command::new("systemctl").arg("kill").arg(format!("container@{}", container_name)))
        },
        Err(e) => Err(e)
    }
}

fn destroy(container_name : &str) -> Result<()> {
    fn log_err(e: Error) {
        println!("Error while destroying container: {:?}", e);
    }

    safe_remove_tree(&profile_dir(container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&format!("/nix/var/nix/gcroots/per-container/{}", container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&container_root(container_name)).unwrap_or_else(log_err);

    std::fs::remove_file(conf_file(container_name)).or_else(|e| match e.kind() {
        std::io::ErrorKind::NotFound => Ok(()),
        _ => Err(IoError(e))
    })
}

fn go() -> Result<i32> {
    // Skip program name
    let mut args = std::env::args().skip(1).peekable();

    let no_destroy = args.peek().map_or(false, |arg| arg == "--no-destroy");
    if no_destroy { args.next(); }

    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument".to_string())));

    let test_args : Vec<String> = args.collect();
    if test_args.len() == 0 {
        return Err(Usage("Missing test args".to_string()));
    }

    let config_file = {
        let p = try!(std::fs::canonicalize(config_file).map_err(|e| Usage(format!("For config file: {}", e))));
        try!(p.to_str().ok_or(Usage("Config file path is invalid UTF-8".to_string()))).to_string()
    };

    try!(system_init());
    // We suppress signals in this process and allow the commander module to deal with child processes
    let trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT]);
    let container_name = try!(create(&config_file));
    let res = match trap.wait(std::time::Instant::now()) {
        None => run_test(&container_name, &test_args),
        Some(n) => Err(Interrupted(n))
    };

    if no_destroy {
        try!(stop(&container_name));
    } else {
        try!(destroy(&container_name));
    }

    res
}

fn main() {
    std::process::exit(go().unwrap_or_else(|e| match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        NixError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        ControlError(msg) => { writeln!(io::stderr(), "{}", msg).unwrap(); 2 },
        Interrupted(SIGINT) => { kill(nix::unistd::getpid(), SIGINT).unwrap(); loop {} }
        Interrupted(n) => 128 + n
    }));
}
<commit_msg>Simplify stop/destroy logic in go()<commit_after>extern crate combine;
extern crate nix; // As in *nix
extern crate rand;
extern crate signal;

mod commander;

// Reference: https://github.com/NixOS/nixpkgs/blob/b07051ce6c3ba3039c89b6755da279002b0c3ace/nixos/modules/virtualisation/nixos-container.pl

use nix::sys::signal::{kill, SIGTERM, SIGINT};
use std::io;
use std::io::Write;
use std::process::Command;

#[derive(Debug)]
enum Error {
    Usage(String),
    NonZero(String, i32),
    ControlError(String),
    IoError(io::Error),
    NixError(nix::Error),
    Interrupted(commander::Signal)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

impl From<nix::Error> for Error {
    fn from(e: nix::Error) -> Self { NixError(e) }
}

impl From<commander::NonZero> for Error {
    fn from(e: commander::NonZero) -> Self { match e { commander::NonZero::NonZero(s, o) => NonZero(s, o), commander::NonZero::Interrupted(n) => Interrupted(n) } }
}

type Result<T> = std::result::Result<T, Error>;

// TODO: why can't this be inferred away
fn run<T>(cmd: &mut Command) -> Result<T> where T: commander::CommandOut { commander::run(cmd) }

fn probably_unique_name(length: usize) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

// Result is of the form "10.233.x"
fn unused_ip_block() -> Result<String> {
    // Can bail early anywhere here because we've got the lock
    let mut used = std::collections::HashSet::new();
    for entry in try!(std::fs::read_dir("/etc/containers")) {
        let entry = try!(entry);
        if try!(entry.file_type()).is_file() {
            let mut str = String::new();
            use std::io::Read;
            try!(try!(std::fs::File::open(entry.path())).read_to_string(&mut str));
            for line in str.lines() {
                use combine::{string, many1, choice, digit, token, eof, Parser, ParserExt};
                let ip_component = || { string("10.233.").with(many1::<String, _>(digit())).skip(token('.')).skip(many1::<String, _>(digit())) };
                if let Ok((c, _)) = choice([string("HOST_ADDRESS="), string("LOCAL_ADDRESS=")]).with(ip_component()).skip(eof()).parse(line) {
                    used.insert(c);
                }
            }
        }
    }
    (0..255).map(|p| p.to_string()).find(|s| !used.contains(s)).map(|s| format!("10.233.{}", s)).ok_or(ControlError("Out of IP addresses".to_string()))
}

fn mkpath(mode : u32, path : &str) -> Result<()> {
    use std::os::unix::fs::DirBuilderExt;
    match std::fs::DirBuilder::new().mode(mode).create(path) {
        Err(e) => if let std::io::ErrorKind::AlreadyExists = e.kind() { Ok(()) } else { Err(IoError(e)) },
        Ok(()) => Ok(())
    }
}

fn system_init() -> Result<()> {
    try!(mkpath(0o0755, "/etc/containers"));
    try!(mkpath(0o0700, "/var/lib/containers"));
    Ok(())
}

fn profile_dir(name : &str) -> String {
    format!("/nix/var/nix/profiles/per-container/{}", name)
}

fn container_root(name : &str) -> String {
    format!("/var/lib/containers/{}", name)
}

fn conf_file(name : &str) -> String {
    format!("/etc/containers/{}.conf", name)
}

fn create(config_file : &str) -> Result<String> {
    fn init_container_unsynced(name : &str) -> Result<()> {
        let ip_block = try!(unused_ip_block());
        // Use create_new to avoid clobbering existing container
        let mut f = try!(std::fs::OpenOptions::new().create_new(true).write(true).open(conf_file(name)));
        write!(&mut f, "PRIVATE_NETWORK=1\nHOST_ADDRESS={0}.1\nLOCAL_ADDRESS={0}.2\nAUTO_START=0\n", ip_block).map_err(IoError)
    }

    fn populate_container(name : &str, config_file : &str) -> Result<()> {
        use std::os::unix::fs::DirBuilderExt;
        // The per-container directory is restricted to prevent users on
        // the host from messing with guest users who happen to have the
        // same uid.
        try!(mkpath(0o0700, "/nix/var/nix/profiles/per-container"));
        let profile_dir = profile_dir(name);
        let mut dir_builder = std::fs::DirBuilder::new();
        try!(dir_builder.mode(0o0755).create(&profile_dir));

        let container_root = container_root(name);
        dir_builder.recursive(true);
        try!(dir_builder.mode(0o0755).create(format!("{}/etc/nixos", container_root)));

        // Write configuration.nix into the container fs
        let configuration_nix = format!("{}/etc/nixos/configuration.nix", container_root);
        {
            let mut f = try!(std::fs::File::create(format!("{}/etc/nixos/configuration.nix", container_root)));
            try!(write!(&mut f, "
    {{ config, lib, pkgs, ... }}:
    with lib;

    {{
        boot.isContainer = true;
        networking.hostName = mkDefault \"{}\";
        networking.useDHCP = false;
        imports = [ {} ];
    }}", name, config_file));
            // TODO: guaranteed sync at end of block?
        }

        // Build the container config
        run(Command::new("nix-env")
            .arg("-p").arg(format!("{}/system", profile_dir))
            .arg("-I").arg(format!("nixos-config={}", configuration_nix))
            .arg("-f").arg("<nixpkgs/nixos>")
            .arg("--set")
            .arg("-A").arg("system")
            .arg("--show-trace"))
    }

    let name = format!("fr{}", probably_unique_name(9));

    // Acquire lock
    use std::os::unix::io::IntoRawFd;
    let lock_fd = try!(std::fs::OpenOptions::new().create(true).append(true).open("/run/lock/nixos-container")).into_raw_fd();
    try!(nix::fcntl::flock(lock_fd, nix::fcntl::FlockArg::LockExclusive));

    let res = init_container_unsynced(&name);

    // Drop lock
    unsafe {
        let _ = nix::libc::close(lock_fd);  // If closing fails, ¯\_(ツ)_/¯
    }

    match res.and_then(|()| populate_container(&name, config_file)) {
        Ok(()) => Ok(name),
        // Don't leave a half-baked container behind
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn run_test(container_name : &str, args : &[String]) -> Result<i32> {
    try!(run(Command::new("systemctl").arg("start").arg(format!("container@{}", container_name))));
    let leader_pid = try!(run(Command::new("machinectl").arg("show").arg(container_name).arg("-p").arg("Leader")).and_then(|o : String| {
        use combine::{string, many1, digit, spaces, eof, Parser, ParserExt};
        string("Leader=").with(many1::<String, _>(digit())).skip(spaces()).skip(eof()).parse(o.as_str())
            .map(|(ds, _)| ds)
            .map_err(|_| ControlError(format!("Bad machinectl output {}", o)))
    }));
    let su_cmd = args.iter().map(|s| format!("'{}'", s.replace("'", "'\\''"))).collect::<Vec<String>>().join(" ");
    match run(Command::new("nsenter")
            .arg("-t").arg(leader_pid)
            .arg("-m").arg("-u").arg("-i").arg("-n").arg("-p")
            .arg("--").arg("/var/setuid-wrappers/su").arg("root").arg("-l").arg("-c").arg(format!("exec {}", su_cmd))) {
        Ok(()) => Ok(0),
        Err(NonZero(_, code)) => Ok(code),
        Err(e) => Err(e)
    }
}

// Remove a directory while recursively unmounting all mounted filesystems within
// that directory and unmounting/removing that directory afterwards as well.
//
// Specified path shouldn't be a mountpoint.
fn safe_remove_tree(path : &str) -> Result<()> {
    if !std::path::Path::new(path).is_dir() {
        return Ok(());
    }
    try!(run(Command::new("find").arg(path)
        .arg("-mindepth").arg("1")
        .arg("-xdev")
        .arg("(")
            .arg("-type").arg("d")
            .arg("-exec").arg("mountpoint").arg("-q").arg("{}").arg(";")
        .arg(")")
        .arg("-exec").arg("umount").arg("-fR").arg("{}").arg("+")));
    try!(run(Command::new("rm").arg("--one-file-system").arg("-rf").arg(path)));

    Ok(())
}

fn stop(container_name : &str) -> Result<()> {
    match run(Command::new("systemctl").arg("stop").arg(format!("container@{}", container_name))) {
        Ok(()) => Ok(()),
        Err(Interrupted(_)) => {
            return run(Command::new("systemctl").arg("kill").arg(format!("container@{}", container_name)))
        },
        Err(e) => Err(e)
    }
}

fn destroy(container_name : &str) -> Result<()> {
    fn log_err(e: Error) {
        println!("Error while destroying container: {:?}", e);
    }

    safe_remove_tree(&profile_dir(container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&format!("/nix/var/nix/gcroots/per-container/{}", container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&container_root(container_name)).unwrap_or_else(log_err);

    std::fs::remove_file(conf_file(container_name)).or_else(|e| match e.kind() {
        std::io::ErrorKind::NotFound => Ok(()),
        _ => Err(IoError(e))
    })
}

fn go() -> Result<i32> {
    // Skip program name
    let mut args = std::env::args().skip(1).peekable();

    let no_destroy = args.peek().map_or(false, |arg| arg == "--no-destroy");
    if no_destroy { args.next(); }

    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument".to_string())));

    let test_args : Vec<String> = args.collect();
    if test_args.len() == 0 {
        return Err(Usage("Missing test args".to_string()));
    }

    let config_file = {
        let p = try!(std::fs::canonicalize(config_file).map_err(|e| Usage(format!("For config file: {}", e))));
        try!(p.to_str().ok_or(Usage("Config file path is invalid UTF-8".to_string()))).to_string()
    };

    try!(system_init());
    // We suppress signals in this process and allow the commander module to deal with child processes
    let trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT]);
    let container_name = try!(create(&config_file));
    let res = match trap.wait(std::time::Instant::now()) {
        None => run_test(&container_name, &test_args),
        Some(n) => Err(Interrupted(n))
    };

    let _ = stop(&container_name);
    if !no_destroy {
        try!(destroy(&container_name));
    }

    res
}

fn main() {
    std::process::exit(go().unwrap_or_else(|e| match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        NixError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        ControlError(msg) => { writeln!(io::stderr(), "{}", msg).unwrap(); 2 },
        Interrupted(SIGINT) => { kill(nix::unistd::getpid(), SIGINT).unwrap(); loop {} }
        Interrupted(n) => 128 + n
    }));
}
<|endoftext|>


<commit_before>extern crate syntex_syntax as syntax;
extern crate rustc_serialize;
extern crate clap;
extern crate walkdir;
extern crate threadpool;
extern crate num_cpus;

#[cfg(test)]
extern crate tempfile;

mod visitor;

use rustc_serialize::{json, Encodable, Encoder};

use clap::{App, Arg, SubCommand, AppSettings};

use syntax::parse::{self, ParseSess};
use syntax::visit;

use std::path::Path;

#[derive(Debug, PartialEq)]
enum MatchKind {
    Struct,
    Method,
    Field,

    Function,

    Constant,
    Static,
    Enum,

    Macro,
}

// Required to convert the `MatchKind` enum fields to lowercase in the JSON output
impl Encodable for MatchKind {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        try!(e.emit_str(match *self {
            MatchKind::Struct => "struct",
            MatchKind::Method => "method",
            MatchKind::Field => "field",

            MatchKind::Function => "function",

            MatchKind::Constant => "constant",
            MatchKind::Static => "static",
            MatchKind::Enum => "enum",

            MatchKind::Macro => "macro",
        }));

        Ok(())
    }
}

#[derive(Debug, RustcEncodable)]
pub struct Match {
    path: String,
    name: String,
    container: String,
    kind: MatchKind,
    line: usize,
}

#[derive(PartialEq)]
enum SearchType {
    // Limited to the specific rust file provided
    Local,
    // Limited to the specific rust file provided and any imported modules
    // This is the default search option
    LocalChildren,
    // Searches the entire crate root for any *.rs files
    Global,
}

fn dump_ast(matches: &clap::ArgMatches) {
    let file = matches.value_of("file").unwrap();

    let session = ParseSess::new();
    let krate = parse::parse_crate_from_file(file.as_ref(), vec![], &session).unwrap();

    // Pretty print the parsed AST
    println!("{:#?}", krate.module);
}

fn search_symbol_global(path: &str, query: &str) -> Vec<Match> {
    use walkdir::WalkDir;
    use threadpool::ThreadPool;
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};

    let crate_root = Path::new(path);

    // Check for the presence of Cargo.toml. When searching globally, the user should search from
    // the crate root as we make some assumptions about the environment.
    match std::fs::metadata(crate_root.join("Cargo.toml")) {
        Ok(_) => {}
        Err(_) => {
            panic!("Failed to open Cargo.toml. When searching globally, you must begin at the \
                    crate root")
        }
    };

    let target_dir = crate_root.join("target");

    let pool = ThreadPool::new(num_cpus::get());
    let (tx, rx) = channel();

    let num_jobs = Arc::new(AtomicUsize::new(0));

    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
        let path = entry.path();

        // Filter out target directory
        if path.starts_with(&target_dir) {
            continue;
        }

        let filename = match path.file_name().and_then(|s| s.to_str()) {
            Some(s) => s,
            None => continue,
        };

        // Filter out anything that isn't a rust source unit
        if !filename.ends_with(".rs") {
            continue;
        }

        if let Some(path) = path.to_str() {
            let path = path.to_owned();
            let query = query.to_owned();

            let tx = tx.clone();
            let num_jobs_clone = num_jobs.clone();
            pool.execute(move || {
                let matches = search_symbol_file(&path, &query, false);
                tx.send(matches).unwrap();

                num_jobs_clone.fetch_add(1, Ordering::SeqCst);
            });
        }
    }

    // Wait for all of the jobs to finish
    while pool.active_count() > 0 {}

    // Fold the list of matches into a single list
    let num_jobs = num_jobs.load(Ordering::SeqCst);
    rx.iter().take(num_jobs).fold(vec![], |mut v, m| {
        v.extend(m);
        v
    })
}

fn search_symbol_file(file: &str, query: &str, search_children: bool) -> Vec<Match> {
    let session = ParseSess::new();
    let krate = match parse::parse_crate_from_file(file.as_ref(), vec![], &session) {
        Ok(krate) => krate,
        Err(_) => return vec![],
    };

    use visitor::SymbolVisitor;
    let mut visitor = SymbolVisitor {
        matches: vec![],
        codemap: session.codemap(),
        search_children: search_children,
        query: query.to_lowercase(),
    };

    visit::walk_crate(&mut visitor, &krate);

    visitor.matches
}

fn search_symbol(matches: &clap::ArgMatches) -> Vec<Match> {
    let file = matches.value_of("file").unwrap();
    let query = matches.value_of("query").unwrap_or_default();

    let mut search_type = SearchType::LocalChildren;
    if matches.is_present("local") {
        search_type = SearchType::Local;
    } else if matches.is_present("global") {
        search_type = SearchType::Global;
    }

    match search_type {
        SearchType::Global => search_symbol_global(file, query),
        SearchType::Local => search_symbol_file(file, query, false),
        SearchType::LocalChildren => search_symbol_file(file, query, true),
    }
}

fn main() {
    let app = App::new("rustsym")
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .settings(&[AppSettings::GlobalVersion, AppSettings::SubcommandRequiredElseHelp])
        .subcommand(SubCommand::with_name("ast")
            .about("Pretty print the AST for the provided rust source file")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("file")
                .required(true)
                .help("The rust source file to print the AST for")))
        .subcommand(SubCommand::with_name("search")
            .about("Search for a symbol")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("local")
                .long("local")
                .short("l")
                .help("Search for symbols found only in the provided rust source file"))
            .arg(Arg::with_name("global")
                .long("global")
                .short("g")
                .conflicts_with("local")
                .help("Search for symbols found in the entire crate"))
            .arg(Arg::with_name("file")
                .required(true)
                .help("The rust source file or crate root to search for symbols in"))
            .arg(Arg::with_name("query").help("Optional string to match symbols against")));
    let matches = app.get_matches();

    if let (cmd, Some(m)) = matches.subcommand() {
        match cmd {
            "ast" => dump_ast(m),
            "search" => {
                let matches = search_symbol(m);

                let encoded = json::encode(&matches).unwrap();
                println!("{}", encoded);
            }
            _ => unreachable!(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Match, MatchKind, search_symbol_file};
    use tempfile::NamedTempFile;

    fn create_source_tmp(src: &'static str) -> NamedTempFile {
        use std::io::Write;

        let mut file = NamedTempFile::new().expect("failed to create temporary file");
        file.write_all(src.as_bytes()).unwrap();

        file
    }

    fn get_symbol_matches(src: &'static str, symbol_name: &str) -> Vec<Match> {
        let file = create_source_tmp(src);
        search_symbol_file(file.path().to_str().unwrap(), symbol_name, false)
    }

    fn test_symbol<F>(src: &'static str, symbol_name: &str, test_fn: &F)
        where F: Fn(&Match)
    {
        let matches = get_symbol_matches(src, symbol_name);
        let candidate: &Match = matches.first().expect("Failed to find any matches");

        test_fn(candidate);
    }

    fn test_symbols<F>(src: &'static str, symbols: Vec<&str>, test_fn: F)
        where F: Fn(&Match)
    {
        for symbol in symbols {
            test_symbol(src, symbol, &test_fn);
        }
    }

    #[test]
    fn check_struct() {
        let src = "struct TestStruct {}";

        test_symbols(src, vec!["test", "TestStruct"], |symbol: &Match| {
            assert_eq!(symbol.name, "TestStruct");
            assert_eq!(symbol.container, "");
            assert_eq!(symbol.kind, MatchKind::Struct);
            assert_eq!(symbol.line, 1);
        });
    }

    #[test]
    fn check_struct_impl() {
        let src = "
            struct TestStruct {}

            impl TestStruct {
            fn test_fn() {}
            }
        ";

        test_symbols(src,
                     vec!["test_fn", "TEST_fn"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_fn");
                         assert_eq!(symbol.container, "TestStruct");
                         assert_eq!(symbol.kind, MatchKind::Method);
                         assert_eq!(symbol.line, 5);
                     });
    }

    #[test]
    fn check_struct_field() {
        let src = "
            struct TestStruct {
            test_field: u32
            }
        ";

        test_symbols(src,
                     vec!["test_field", "field"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_field");
                         assert_eq!(symbol.container, "TestStruct");
                         assert_eq!(symbol.kind, MatchKind::Field);
                         assert_eq!(symbol.line, 3);
                     });
    }

    #[test]
    fn check_function() {
        let src = "fn test_fn() {}";

        test_symbols(src,
                     vec!["test_fn", "TEST_fn"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_fn");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Function);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_constant() {
        let src = "const ConstSymbol: u32 = 0;";

        test_symbols(src,
                     vec!["ConstSymbol", "symbol"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "ConstSymbol");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Constant);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_static() {
        let src = "static StaticSymbol: u32 = 0;";

        test_symbols(src,
                     vec!["StaticSymbol", "symbol"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "StaticSymbol");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Static);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_enum() {
        let src = "enum TestEnum {}";

        test_symbols(src,
                     vec!["TestEnum", "test"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "TestEnum");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Enum);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_macro() {
        let src = "
            macro_rules! test_macro {
            () => ()
            }
        ";

        test_symbols(src,
                     vec!["test_macro", "test"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_macro");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Macro);
                         assert_eq!(symbol.line, 2);
                     });
    }

    #[test]
    fn check_struct_inclusive() {
        let src = "
            struct TestStruct {
                test_field: u32
            }

            impl TestStruct {
                fn test_fn() {}
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 3);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestStruct");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Struct);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "test_field");
        assert_eq!(symbol.container, "TestStruct");
        assert_eq!(symbol.kind, MatchKind::Field);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "test_fn");
        assert_eq!(symbol.container, "TestStruct");
        assert_eq!(symbol.kind, MatchKind::Method);
        assert_eq!(symbol.line, 7);
    }

    #[test]
    fn check_trait() {
        let src = "
            trait TestTrait {
                fn test_fn();
                const test_constant: i32;
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 3);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestTrait");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Struct);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "test_fn");
        assert_eq!(symbol.container, "TestTrait");
        assert_eq!(symbol.kind, MatchKind::Method);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "test_constant");
        assert_eq!(symbol.container, "TestTrait");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 4);
    }

    #[test]
    fn check_enum_inclusive() {
        let src = "
            enum TestEnum {
                Member1,
                Member2(u32),
                Member3(TestEnum)
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 4);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestEnum");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Enum);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "Member1");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "Member2");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 4);

        let ref symbol = matches[3];
        assert_eq!(symbol.name, "Member3");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 5);
    }
}<commit_msg>Fixed a threading issue with the global search flag where the process could terminate before the jobs had completed. Fixes #2<commit_after>extern crate syntex_syntax as syntax;
extern crate rustc_serialize;
extern crate clap;
extern crate walkdir;
extern crate syncbox;
extern crate num_cpus;

#[cfg(test)]
extern crate tempfile;

mod visitor;

use rustc_serialize::{json, Encodable, Encoder};

use clap::{App, Arg, SubCommand, AppSettings};

use syntax::parse::{self, ParseSess};
use syntax::visit;

use std::path::Path;

#[derive(Debug, PartialEq)]
enum MatchKind {
    Struct,
    Method,
    Field,

    Function,

    Constant,
    Static,
    Enum,

    Macro,
}

// Required to convert the `MatchKind` enum fields to lowercase in the JSON output
impl Encodable for MatchKind {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        try!(e.emit_str(match *self {
            MatchKind::Struct => "struct",
            MatchKind::Method => "method",
            MatchKind::Field => "field",

            MatchKind::Function => "function",

            MatchKind::Constant => "constant",
            MatchKind::Static => "static",
            MatchKind::Enum => "enum",

            MatchKind::Macro => "macro",
        }));

        Ok(())
    }
}

#[derive(Debug, RustcEncodable)]
pub struct Match {
    path: String,
    name: String,
    container: String,
    kind: MatchKind,
    line: usize,
}

#[derive(PartialEq)]
enum SearchType {
    // Limited to the specific rust file provided
    Local,
    // Limited to the specific rust file provided and any imported modules
    // This is the default search option
    LocalChildren,
    // Searches the entire crate root for any *.rs files
    Global,
}

fn dump_ast(matches: &clap::ArgMatches) {
    let file = matches.value_of("file").unwrap();

    let session = ParseSess::new();
    let krate = parse::parse_crate_from_file(file.as_ref(), vec![], &session).unwrap();

    // Pretty print the parsed AST
    println!("{:#?}", krate.module);
}

fn search_symbol_global(path: &str, query: &str) -> Vec<Match> {
    use walkdir::WalkDir;
    use syncbox::{ThreadPool, Run};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};

    let crate_root = Path::new(path);

    // Check for the presence of Cargo.toml. When searching globally, the user should search from
    // the crate root as we make some assumptions about the environment.
    match std::fs::metadata(crate_root.join("Cargo.toml")) {
        Ok(_) => {}
        Err(_) => {
            panic!("Failed to open Cargo.toml. When searching globally, you must begin at the \
                    crate root")
        }
    };

    let target_dir = crate_root.join("target");

    let started_jobs = Arc::new(AtomicUsize::new(0));
    let completed_jobs = Arc::new(AtomicUsize::new(0));

    let (tx, rx) = channel();

    let mut expected_jobs = 0;

    let pool = ThreadPool::fixed_size(num_cpus::get() as u32);
    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
        let path = entry.path();

        // Filter out target directory
        if path.starts_with(&target_dir) {
            continue;
        }

        let filename = match path.file_name().and_then(|s| s.to_str()) {
            Some(s) => s,
            None => continue,
        };

        // Filter out anything that isn't a rust source unit
        if !filename.ends_with(".rs") {
            continue;
        }

        if let Some(path) = path.to_str() {
            let path = path.to_owned();
            let query = query.to_owned();

            let started_jobs_clone = started_jobs.clone();
            let completed_jobs_clone = completed_jobs.clone();
            let tx = tx.clone();

            pool.run(move || {
                // Indicate that this job has started. The job may not actually
                // finish as serde can panic on malformed Rust syntax, but
                // `completed_jobs` can catch this.
                started_jobs_clone.fetch_add(1, Ordering::SeqCst);

                let matches = search_symbol_file(&path, &query, false);

                tx.send(matches).unwrap();

                // Indicate that we have a completed result
                completed_jobs_clone.fetch_add(1, Ordering::SeqCst);
            });

            expected_jobs += 1;
        }
    }

    // Wait for all of the jobs to start
    while started_jobs.load(Ordering::SeqCst) != expected_jobs {}

    // Stop any new jobs and wait for all existing jobs to finish
    pool.shutdown_now();
    pool.await_termination();

    // Fold the list of matches into a single list
    let completed_jobs = completed_jobs.load(Ordering::Relaxed);
    rx.iter().take(completed_jobs).fold(vec![], |mut v, m| {
        v.extend(m);
        v
    })
}

fn search_symbol_file(file: &str, query: &str, search_children: bool) -> Vec<Match> {
    let session = ParseSess::new();
    let krate = match parse::parse_crate_from_file(file.as_ref(), vec![], &session) {
        Ok(krate) => krate,
        Err(_) => return vec![],
    };

    use visitor::SymbolVisitor;
    let mut visitor = SymbolVisitor {
        matches: vec![],
        codemap: session.codemap(),
        search_children: search_children,
        query: query.to_lowercase(),
    };

    visit::walk_crate(&mut visitor, &krate);

    visitor.matches
}

fn search_symbol(matches: &clap::ArgMatches) -> Vec<Match> {
    let file = matches.value_of("file").unwrap();
    let query = matches.value_of("query").unwrap_or_default();

    let mut search_type = SearchType::LocalChildren;
    if matches.is_present("local") {
        search_type = SearchType::Local;
    } else if matches.is_present("global") {
        search_type = SearchType::Global;
    }

    match search_type {
        SearchType::Global => search_symbol_global(file, query),
        SearchType::Local => search_symbol_file(file, query, false),
        SearchType::LocalChildren => search_symbol_file(file, query, true),
    }
}

fn main() {
    let app = App::new("rustsym")
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .settings(&[AppSettings::GlobalVersion, AppSettings::SubcommandRequiredElseHelp])
        .subcommand(SubCommand::with_name("ast")
            .about("Pretty print the AST for the provided rust source file")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("file")
                .required(true)
                .help("The rust source file to print the AST for")))
        .subcommand(SubCommand::with_name("search")
            .about("Search for a symbol")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(Arg::with_name("local")
                .long("local")
                .short("l")
                .help("Search for symbols found only in the provided rust source file"))
            .arg(Arg::with_name("global")
                .long("global")
                .short("g")
                .conflicts_with("local")
                .help("Search for symbols found in the entire crate"))
            .arg(Arg::with_name("file")
                .required(true)
                .help("The rust source file or crate root to search for symbols in"))
            .arg(Arg::with_name("query").help("Optional string to match symbols against")));
    let matches = app.get_matches();

    if let (cmd, Some(m)) = matches.subcommand() {
        match cmd {
            "ast" => dump_ast(m),
            "search" => {
                let matches = search_symbol(m);

                let encoded = json::encode(&matches).unwrap();
                println!("{}", encoded);
            }
            _ => unreachable!(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Match, MatchKind, search_symbol_file};
    use tempfile::NamedTempFile;

    fn create_source_tmp(src: &'static str) -> NamedTempFile {
        use std::io::Write;

        let mut file = NamedTempFile::new().expect("failed to create temporary file");
        file.write_all(src.as_bytes()).unwrap();

        file
    }

    fn get_symbol_matches(src: &'static str, symbol_name: &str) -> Vec<Match> {
        let file = create_source_tmp(src);
        search_symbol_file(file.path().to_str().unwrap(), symbol_name, false)
    }

    fn test_symbol<F>(src: &'static str, symbol_name: &str, test_fn: &F)
        where F: Fn(&Match)
    {
        let matches = get_symbol_matches(src, symbol_name);
        let candidate: &Match = matches.first().expect("Failed to find any matches");

        test_fn(candidate);
    }

    fn test_symbols<F>(src: &'static str, symbols: Vec<&str>, test_fn: F)
        where F: Fn(&Match)
    {
        for symbol in symbols {
            test_symbol(src, symbol, &test_fn);
        }
    }

    #[test]
    fn check_struct() {
        let src = "struct TestStruct {}";

        test_symbols(src, vec!["test", "TestStruct"], |symbol: &Match| {
            assert_eq!(symbol.name, "TestStruct");
            assert_eq!(symbol.container, "");
            assert_eq!(symbol.kind, MatchKind::Struct);
            assert_eq!(symbol.line, 1);
        });
    }

    #[test]
    fn check_struct_impl() {
        let src = "
            struct TestStruct {}

            impl TestStruct {
            fn test_fn() {}
            }
        ";

        test_symbols(src,
                     vec!["test_fn", "TEST_fn"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_fn");
                         assert_eq!(symbol.container, "TestStruct");
                         assert_eq!(symbol.kind, MatchKind::Method);
                         assert_eq!(symbol.line, 5);
                     });
    }

    #[test]
    fn check_struct_field() {
        let src = "
            struct TestStruct {
            test_field: u32
            }
        ";

        test_symbols(src,
                     vec!["test_field", "field"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_field");
                         assert_eq!(symbol.container, "TestStruct");
                         assert_eq!(symbol.kind, MatchKind::Field);
                         assert_eq!(symbol.line, 3);
                     });
    }

    #[test]
    fn check_function() {
        let src = "fn test_fn() {}";

        test_symbols(src,
                     vec!["test_fn", "TEST_fn"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_fn");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Function);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_constant() {
        let src = "const ConstSymbol: u32 = 0;";

        test_symbols(src,
                     vec!["ConstSymbol", "symbol"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "ConstSymbol");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Constant);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_static() {
        let src = "static StaticSymbol: u32 = 0;";

        test_symbols(src,
                     vec!["StaticSymbol", "symbol"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "StaticSymbol");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Static);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_enum() {
        let src = "enum TestEnum {}";

        test_symbols(src,
                     vec!["TestEnum", "test"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "TestEnum");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Enum);
                         assert_eq!(symbol.line, 1);
                     });
    }

    #[test]
    fn check_macro() {
        let src = "
            macro_rules! test_macro {
            () => ()
            }
        ";

        test_symbols(src,
                     vec!["test_macro", "test"],
                     &|symbol: &Match| {
                         assert_eq!(symbol.name, "test_macro");
                         assert_eq!(symbol.container, "");
                         assert_eq!(symbol.kind, MatchKind::Macro);
                         assert_eq!(symbol.line, 2);
                     });
    }

    #[test]
    fn check_struct_inclusive() {
        let src = "
            struct TestStruct {
                test_field: u32
            }

            impl TestStruct {
                fn test_fn() {}
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 3);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestStruct");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Struct);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "test_field");
        assert_eq!(symbol.container, "TestStruct");
        assert_eq!(symbol.kind, MatchKind::Field);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "test_fn");
        assert_eq!(symbol.container, "TestStruct");
        assert_eq!(symbol.kind, MatchKind::Method);
        assert_eq!(symbol.line, 7);
    }

    #[test]
    fn check_trait() {
        let src = "
            trait TestTrait {
                fn test_fn();
                const test_constant: i32;
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 3);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestTrait");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Struct);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "test_fn");
        assert_eq!(symbol.container, "TestTrait");
        assert_eq!(symbol.kind, MatchKind::Method);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "test_constant");
        assert_eq!(symbol.container, "TestTrait");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 4);
    }

    #[test]
    fn check_enum_inclusive() {
        let src = "
            enum TestEnum {
                Member1,
                Member2(u32),
                Member3(TestEnum)
            }
        ";

        let matches = get_symbol_matches(src, "");
        assert!(matches.len() == 4);

        let ref symbol = matches[0];
        assert_eq!(symbol.name, "TestEnum");
        assert_eq!(symbol.container, "");
        assert_eq!(symbol.kind, MatchKind::Enum);
        assert_eq!(symbol.line, 2);

        let ref symbol = matches[1];
        assert_eq!(symbol.name, "Member1");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 3);

        let ref symbol = matches[2];
        assert_eq!(symbol.name, "Member2");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 4);

        let ref symbol = matches[3];
        assert_eq!(symbol.name, "Member3");
        assert_eq!(symbol.container, "TestEnum");
        assert_eq!(symbol.kind, MatchKind::Constant);
        assert_eq!(symbol.line, 5);
    }
}<|endoftext|>