<commit_before>//! Support for capturing a stack backtrace of an OS thread
//!
//! This module contains the support necessary to capture a stack backtrace of a
//! running OS thread from the OS thread itself. The `Backtrace` type supports
//! capturing a stack trace via the `Backtrace::capture` and
//! `Backtrace::force_capture` functions.
//!
//! A backtrace is typically quite handy to attach to errors (e.g. types
//! implementing `std::error::Error`) to get a causal chain of where an error
//! was generated.
//!
//! > **Note**: this module is unstable and is designed in [RFC 2504], and you
//! > can learn more about its status in the [tracking issue].
//!
//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md
//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487
//!
//! ## Accuracy
//!
//! Backtraces are attempted to be as accurate as possible, but no guarantees
//! are provided about the exact accuracy of a backtrace. Instruction pointers,
//! symbol names, filenames, line numbers, etc, may all be incorrect when
//! reported. Accuracy is attempted on a best-effort basis, however, and bugs
//! are always welcome to indicate areas of improvement!
//!
//! For most platforms a backtrace with a filename/line number requires that
//! programs be compiled with debug information. Without debug information
//! filenames/line numbers will not be reported.
//!
//! ## Platform support
//!
//! Not all platforms that libstd compiles for support capturing backtraces.
//! Some platforms simply do nothing when capturing a backtrace. To check
//! whether the platform supports capturing backtraces you can consult the
//! `BacktraceStatus` enum as a result of `Backtrace::status`.
//!
//! Like above with accuracy platform support is done on a best effort basis.
//! Sometimes libraries may not be available at runtime or something may go
//! wrong which would cause a backtrace to not be captured. Please feel free to
//! report issues with platforms where a backtrace cannot be captured though!
//!
//! ## Environment Variables
//!
//! The `Backtrace::capture` function may not actually capture a backtrace by
//! default. Its behavior is governed by two environment variables:
//!
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
//!   will never capture a backtrace. Any other value this is set to will enable
//!   `Backtrace::capture`.
//!
//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
//!   is consulted with the same rules of `RUST_LIB_BACKTRACE`.
//!
//! * If neither of the above env vars are set, then `Backtrace::capture` will
//!   be disabled.
//!
//! Capturing a backtrace can be a quite expensive runtime operation, so the
//! environment variables allow either forcibly disabling this runtime
//! performance hit or allow selectively enabling it in some programs.
//!
//! Note that the `Backtrace::force_capture` function can be used to ignore
//! these environment variables. Also note that the state of environment
//! variables is cached once the first backtrace is created, so altering
//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change
//! how backtraces are captured.

#![unstable(feature = "backtrace", issue = "53487")]

// NB: A note on resolution of a backtrace:
//
// Backtraces primarily happen in two steps, one is where we actually capture
// the stack backtrace, giving us a list of instruction pointers corresponding
// to stack frames. Next we take these instruction pointers and, one-by-one,
// turn them into a human readable name (like `main`).
//
// The first phase can be somewhat expensive (walking the stack), especially
// on MSVC where debug information is consulted to return inline frames each as
// their own frame. The second phase, however, is almost always extremely
// expensive (on the order of milliseconds sometimes) when it's consulting debug
// information.
//
// We attempt to amortize this cost as much as possible by delaying resolution
// of an address to a human readable name for as long as possible. When
// `Backtrace::create` is called to capture a backtrace it doesn't actually
// perform any symbol resolution, but rather we lazily resolve symbols only just
// before they're needed for printing. This way we can make capturing a
// backtrace and throwing it away much cheaper, but actually printing a
// backtrace is still basically the same cost.
//
// This strategy comes at the cost of some synchronization required inside of a
// `Backtrace`, but that's a relatively small price to pay relative to capturing
// a backtrace or actually symbolizing it.

use crate::env;
use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use crate::sync::Mutex;
use crate::sys_common::backtrace::{lock, output_filename};
use crate::vec::Vec;
use backtrace::BytesOrWideString;
use backtrace_rs as backtrace;

/// A captured OS thread stack backtrace.
///
/// This type represents a stack backtrace for an OS thread captured at a
/// previous point in time. In some instances the `Backtrace` type may
/// internally be empty due to configuration. For more information see
/// `Backtrace::capture`.
pub struct Backtrace {
    inner: Inner,
}

/// The current status of a backtrace, indicating whether it was captured or
/// whether it is empty for some other reason.
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum BacktraceStatus {
    /// Capturing a backtrace is not supported, likely because it's not
    /// implemented for the current platform.
    Unsupported,
    /// Capturing a backtrace has been disabled through either the
    /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
    Disabled,
    /// A backtrace has been captured and the `Backtrace` should print
    /// reasonable information when rendered.
    Captured,
}

enum Inner {
    Unsupported,
    Disabled,
    Captured(Mutex<Capture>),
}

struct Capture {
    actual_start: usize,
    resolved: bool,
    frames: Vec<BacktraceFrame>,
}

fn _assert_send_sync() {
    fn _assert<T: Send + Sync>() {}
    _assert::<Backtrace>();
}

struct BacktraceFrame {
    frame: backtrace::Frame,
    symbols: Vec<BacktraceSymbol>,
}

struct BacktraceSymbol {
    name: Option<Vec<u8>>,
    filename: Option<BytesOrWide>,
    lineno: Option<u32>,
}

enum BytesOrWide {
    Bytes(Vec<u8>),
    Wide(Vec<u16>),
}

impl fmt::Debug for Backtrace {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut capture = match &self.inner {
            Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
            Inner::Disabled => return fmt.write_str("disabled backtrace"),
            Inner::Captured(c) => c.lock().unwrap(),
        };
        capture.resolve();

        let frames = if fmt.alternate() {
            &capture.frames[..]
        } else {
            &capture.frames[capture.actual_start..]
        };

        write!(fmt, "Backtrace ")?;

        let mut dbg = fmt.debug_list();

        for frame in frames {
            if frame.frame.ip().is_null() {
                continue;
            }

            dbg.entries(&frame.symbols);
        }

        dbg.finish()
    }
}

impl fmt::Debug for BacktraceSymbol {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{{ ")?;

        if let Some(fn_name) = self.name.as_ref().map(|b| backtrace::SymbolName::new(b)) {
            write!(fmt, "fn: ")?;
            fmt::Display::fmt(&fn_name, fmt)?;
        } else {
            write!(fmt, "fn: <unknown>")?;
        }

        if let Some(fname) = self.filename.as_ref() {
            write!(fmt, ", file: {:?}", fname)?;
        }

        if let Some(line) = self.lineno.as_ref() {
            write!(fmt, ", line: {:?}", line)?;
        }

        write!(fmt, " }}")
    }
}

impl fmt::Debug for BytesOrWide {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        output_filename(
            fmt,
            match self {
                BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
                BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
            },
            if fmt.alternate() { backtrace::PrintFmt::Full } else { backtrace::PrintFmt::Short },
            crate::env::current_dir().as_ref().ok(),
        )
    }
}

impl Backtrace {
    /// Returns whether backtrace captures are enabled through environment
    /// variables.
    fn enabled() -> bool {
        // Cache the result of reading the environment variables to make
        // backtrace captures speedy, because otherwise reading environment
        // variables every time can be somewhat slow.
        static ENABLED: AtomicUsize = AtomicUsize::new(0);
        match ENABLED.load(SeqCst) {
            0 => {}
            1 => return false,
            _ => return true,
        }
        let enabled = match env::var("RUST_LIB_BACKTRACE") {
            Ok(s) => s != "0",
            Err(_) => match env::var("RUST_BACKTRACE") {
                Ok(s) => s != "0",
                Err(_) => false,
            },
        };
        ENABLED.store(enabled as usize + 1, SeqCst);
        return enabled;
    }

    /// Capture a stack backtrace of the current thread.
    ///
    /// This function will capture a stack backtrace of the current OS thread of
    /// execution, returning a `Backtrace` type which can be later used to print
    /// the entire stack trace or render it to a string.
    ///
    /// This function will be a noop if the `RUST_BACKTRACE` or
    /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either
    /// environment variable is set and enabled then this function will actually
    /// capture a backtrace. Capturing a backtrace can be both memory intensive
    /// and slow, so these environment variables allow liberally using
    /// `Backtrace::capture` and only incurring a slowdown when the environment
    /// variables are set.
    ///
    /// To forcibly capture a backtrace regardless of environment variables, use
    /// the `Backtrace::force_capture` function.
    #[inline(never)] // want to make sure there's a frame here to remove
    pub fn capture() -> Backtrace {
        if !Backtrace::enabled() {
            return Backtrace { inner: Inner::Disabled };
        }
        Backtrace::create(Backtrace::capture as usize)
    }

    /// Forcibly captures a full backtrace, regardless of environment variable
    /// configuration.
    ///
    /// This function behaves the same as `capture` except that it ignores the
    /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment
    /// variables, always capturing a backtrace.
    ///
    /// Note that capturing a backtrace can be an expensive operation on some
    /// platforms, so this should be used with caution in performance-sensitive
    /// parts of code.
    #[inline(never)] // want to make sure there's a frame here to remove
    pub fn force_capture() -> Backtrace {
        Backtrace::create(Backtrace::force_capture as usize)
    }

    // Capture a backtrace which start just before the function addressed by
    // `ip`
    fn create(ip: usize) -> Backtrace {
        let _lock = lock();
        let mut frames = Vec::new();
        let mut actual_start = None;
        unsafe {
            backtrace::trace_unsynchronized(|frame| {
                frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new() });
                if frame.symbol_address() as usize == ip && actual_start.is_none() {
                    actual_start = Some(frames.len());
                }
                true
            });
        }

        // If no frames came out assume that this is an unsupported platform
        // since `backtrace` doesn't provide a way of learning this right now,
        // and this should be a good enough approximation.
        let inner = if frames.len() == 0 {
            Inner::Unsupported
        } else {
            Inner::Captured(Mutex::new(Capture {
                actual_start: actual_start.unwrap_or(0),
                frames,
                resolved: false,
            }))
        };

        Backtrace { inner }
    }

    /// Returns the status of this backtrace, indicating whether this backtrace
    /// request was unsupported, disabled, or a stack trace was actually
    /// captured.
    pub fn status(&self) -> BacktraceStatus {
        match self.inner {
            Inner::Unsupported => BacktraceStatus::Unsupported,
            Inner::Disabled => BacktraceStatus::Disabled,
            Inner::Captured(_) => BacktraceStatus::Captured,
        }
    }
}

impl fmt::Display for Backtrace {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut capture = match &self.inner {
            Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
            Inner::Disabled => return fmt.write_str("disabled backtrace"),
            Inner::Captured(c) => c.lock().unwrap(),
        };
        capture.resolve();

        let full = fmt.alternate();
        let (frames, style) = if full {
            (&capture.frames[..], backtrace::PrintFmt::Full)
        } else {
            (&capture.frames[capture.actual_start..], backtrace::PrintFmt::Short)
        };

        // When printing paths we try to strip the cwd if it exists, otherwise
        // we just print the path as-is. Note that we also only do this for the
        // short format, because if it's full we presumably want to print
        // everything.
        let cwd = crate::env::current_dir();
        let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
            output_filename(fmt, path, style, cwd.as_ref().ok())
        };

        let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path);
        f.add_context()?;
        for frame in frames {
            let mut f = f.frame();
            if frame.symbols.is_empty() {
                f.print_raw(frame.frame.ip(), None, None, None)?;
            } else {
                for symbol in frame.symbols.iter() {
                    f.print_raw(
                        frame.frame.ip(),
                        symbol.name.as_ref().map(|b| backtrace::SymbolName::new(b)),
                        symbol.filename.as_ref().map(|b| match b {
                            BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
                            BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
                        }),
                        symbol.lineno,
                    )?;
                }
            }
        }
        f.finish()?;
        Ok(())
    }
}

impl Capture {
    fn resolve(&mut self) {
        // If we're already resolved, nothing to do!
        if self.resolved {
            return;
        }
        self.resolved = true;

        // Use the global backtrace lock to synchronize this as it's a
        // requirement of the `backtrace` crate, and then actually resolve
        // everything.
        let _lock = lock();
        for frame in self.frames.iter_mut() {
            let symbols = &mut frame.symbols;
            unsafe {
                backtrace::resolve_frame_unsynchronized(&frame.frame, |symbol| {
                    symbols.push(BacktraceSymbol {
                        name: symbol.name().map(|m| m.as_bytes().to_vec()),
                        filename: symbol.filename_raw().map(|b| match b {
                            BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
                            BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
                        }),
                        lineno: symbol.lineno(),
                    });
                });
            }
        }
    }
}

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

    #[test]
    fn debug_backtrace_fmt() {
        let bt = Backtrace::capture();
        eprintln!("uncaptured: {:?}", bt);
        let bt = Backtrace::force_capture();
        eprintln!("captured: {:?}", bt);
        eprintln!("display print: {}", bt);
        eprintln!("display print alt: {:#}", bt);
        eprintln!("resolved: {:?}", bt);
        eprintln!("resolved alt: {:#?}", bt);
        unimplemented!();
    }
}
<commit_msg>rule over the code in libstd with an iron fist<commit_after>//! Support for capturing a stack backtrace of an OS thread
//!
//! This module contains the support necessary to capture a stack backtrace of a
//! running OS thread from the OS thread itself. The `Backtrace` type supports
//! capturing a stack trace via the `Backtrace::capture` and
//! `Backtrace::force_capture` functions.
//!
//! A backtrace is typically quite handy to attach to errors (e.g. types
//! implementing `std::error::Error`) to get a causal chain of where an error
//! was generated.
//!
//! > **Note**: this module is unstable and is designed in [RFC 2504], and you
//! > can learn more about its status in the [tracking issue].
//!
//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md
//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487
//!
//! ## Accuracy
//!
//! Backtraces are attempted to be as accurate as possible, but no guarantees
//! are provided about the exact accuracy of a backtrace. Instruction pointers,
//! symbol names, filenames, line numbers, etc, may all be incorrect when
//! reported. Accuracy is attempted on a best-effort basis, however, and bugs
//! are always welcome to indicate areas of improvement!
//!
//! For most platforms a backtrace with a filename/line number requires that
//! programs be compiled with debug information. Without debug information
//! filenames/line numbers will not be reported.
//!
//! ## Platform support
//!
//! Not all platforms that libstd compiles for support capturing backtraces.
//! Some platforms simply do nothing when capturing a backtrace. To check
//! whether the platform supports capturing backtraces you can consult the
//! `BacktraceStatus` enum as a result of `Backtrace::status`.
//!
//! Like above with accuracy platform support is done on a best effort basis.
//! Sometimes libraries may not be available at runtime or something may go
//! wrong which would cause a backtrace to not be captured. Please feel free to
//! report issues with platforms where a backtrace cannot be captured though!
//!
//! ## Environment Variables
//!
//! The `Backtrace::capture` function may not actually capture a backtrace by
//! default. Its behavior is governed by two environment variables:
//!
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
//!   will never capture a backtrace. Any other value this is set to will enable
//!   `Backtrace::capture`.
//!
//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
//!   is consulted with the same rules of `RUST_LIB_BACKTRACE`.
//!
//! * If neither of the above env vars are set, then `Backtrace::capture` will
//!   be disabled.
//!
//! Capturing a backtrace can be a quite expensive runtime operation, so the
//! environment variables allow either forcibly disabling this runtime
//! performance hit or allow selectively enabling it in some programs.
//!
//! Note that the `Backtrace::force_capture` function can be used to ignore
//! these environment variables. Also note that the state of environment
//! variables is cached once the first backtrace is created, so altering
//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change
//! how backtraces are captured.

#![unstable(feature = "backtrace", issue = "53487")]

// NB: A note on resolution of a backtrace:
//
// Backtraces primarily happen in two steps, one is where we actually capture
// the stack backtrace, giving us a list of instruction pointers corresponding
// to stack frames. Next we take these instruction pointers and, one-by-one,
// turn them into a human readable name (like `main`).
//
// The first phase can be somewhat expensive (walking the stack), especially
// on MSVC where debug information is consulted to return inline frames each as
// their own frame. The second phase, however, is almost always extremely
// expensive (on the order of milliseconds sometimes) when it's consulting debug
// information.
//
// We attempt to amortize this cost as much as possible by delaying resolution
// of an address to a human readable name for as long as possible. When
// `Backtrace::create` is called to capture a backtrace it doesn't actually
// perform any symbol resolution, but rather we lazily resolve symbols only just
// before they're needed for printing. This way we can make capturing a
// backtrace and throwing it away much cheaper, but actually printing a
// backtrace is still basically the same cost.
//
// This strategy comes at the cost of some synchronization required inside of a
// `Backtrace`, but that's a relatively small price to pay relative to capturing
// a backtrace or actually symbolizing it.

use crate::env;
use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use crate::sync::Mutex;
use crate::sys_common::backtrace::{lock, output_filename};
use crate::vec::Vec;
use backtrace::BytesOrWideString;
use backtrace_rs as backtrace;

/// A captured OS thread stack backtrace.
///
/// This type represents a stack backtrace for an OS thread captured at a
/// previous point in time. In some instances the `Backtrace` type may
/// internally be empty due to configuration. For more information see
/// `Backtrace::capture`.
pub struct Backtrace {
    inner: Inner,
}

/// The current status of a backtrace, indicating whether it was captured or
/// whether it is empty for some other reason.
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum BacktraceStatus {
    /// Capturing a backtrace is not supported, likely because it's not
    /// implemented for the current platform.
    Unsupported,
    /// Capturing a backtrace has been disabled through either the
    /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
    Disabled,
    /// A backtrace has been captured and the `Backtrace` should print
    /// reasonable information when rendered.
    Captured,
}

enum Inner {
    Unsupported,
    Disabled,
    Captured(Mutex<Capture>),
}

struct Capture {
    actual_start: usize,
    resolved: bool,
    frames: Vec<BacktraceFrame>,
}

fn _assert_send_sync() {
    fn _assert<T: Send + Sync>() {}
    _assert::<Backtrace>();
}

struct BacktraceFrame {
    frame: backtrace::Frame,
    symbols: Vec<BacktraceSymbol>,
}

struct BacktraceSymbol {
    name: Option<Vec<u8>>,
    filename: Option<BytesOrWide>,
    lineno: Option<u32>,
}

enum BytesOrWide {
    Bytes(Vec<u8>),
    Wide(Vec<u16>),
}

impl fmt::Debug for Backtrace {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut capture = match &self.inner {
            Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
            Inner::Disabled => return fmt.write_str("disabled backtrace"),
            Inner::Captured(c) => c.lock().unwrap(),
        };
        capture.resolve();

        let frames = &capture.frames[capture.actual_start..];

        write!(fmt, "Backtrace ")?;

        let mut dbg = fmt.debug_list();

        for frame in frames {
            if frame.frame.ip().is_null() {
                continue;
            }

            dbg.entries(&frame.symbols);
        }

        dbg.finish()
    }
}

impl fmt::Debug for BacktraceSymbol {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{{ ")?;

        if let Some(fn_name) = self.name.as_ref().map(|b| backtrace::SymbolName::new(b)) {
            write!(fmt, "fn: \"{}\"", fn_name)?;
        } else {
            write!(fmt, "fn: \"<unknown>\"")?;
        }

        if let Some(fname) = self.filename.as_ref() {
            write!(fmt, ", file: {:?}", fname)?;
        }

        if let Some(line) = self.lineno.as_ref() {
            write!(fmt, ", line: {:?}", line)?;
        }

        write!(fmt, " }}")
    }
}

impl fmt::Debug for BytesOrWide {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        output_filename(
            fmt,
            match self {
                BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
                BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
            },
            backtrace::PrintFmt::Short,
            crate::env::current_dir().as_ref().ok(),
        )
    }
}

impl Backtrace {
    /// Returns whether backtrace captures are enabled through environment
    /// variables.
    fn enabled() -> bool {
        // Cache the result of reading the environment variables to make
        // backtrace captures speedy, because otherwise reading environment
        // variables every time can be somewhat slow.
        static ENABLED: AtomicUsize = AtomicUsize::new(0);
        match ENABLED.load(SeqCst) {
            0 => {}
            1 => return false,
            _ => return true,
        }
        let enabled = match env::var("RUST_LIB_BACKTRACE") {
            Ok(s) => s != "0",
            Err(_) => match env::var("RUST_BACKTRACE") {
                Ok(s) => s != "0",
                Err(_) => false,
            },
        };
        ENABLED.store(enabled as usize + 1, SeqCst);
        return enabled;
    }

    /// Capture a stack backtrace of the current thread.
    ///
    /// This function will capture a stack backtrace of the current OS thread of
    /// execution, returning a `Backtrace` type which can be later used to print
    /// the entire stack trace or render it to a string.
    ///
    /// This function will be a noop if the `RUST_BACKTRACE` or
    /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either
    /// environment variable is set and enabled then this function will actually
    /// capture a backtrace. Capturing a backtrace can be both memory intensive
    /// and slow, so these environment variables allow liberally using
    /// `Backtrace::capture` and only incurring a slowdown when the environment
    /// variables are set.
    ///
    /// To forcibly capture a backtrace regardless of environment variables, use
    /// the `Backtrace::force_capture` function.
    #[inline(never)] // want to make sure there's a frame here to remove
    pub fn capture() -> Backtrace {
        if !Backtrace::enabled() {
            return Backtrace { inner: Inner::Disabled };
        }
        Backtrace::create(Backtrace::capture as usize)
    }

    /// Forcibly captures a full backtrace, regardless of environment variable
    /// configuration.
    ///
    /// This function behaves the same as `capture` except that it ignores the
    /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment
    /// variables, always capturing a backtrace.
    ///
    /// Note that capturing a backtrace can be an expensive operation on some
    /// platforms, so this should be used with caution in performance-sensitive
    /// parts of code.
    #[inline(never)] // want to make sure there's a frame here to remove
    pub fn force_capture() -> Backtrace {
        Backtrace::create(Backtrace::force_capture as usize)
    }

    // Capture a backtrace which start just before the function addressed by
    // `ip`
    fn create(ip: usize) -> Backtrace {
        let _lock = lock();
        let mut frames = Vec::new();
        let mut actual_start = None;
        unsafe {
            backtrace::trace_unsynchronized(|frame| {
                frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new() });
                if frame.symbol_address() as usize == ip && actual_start.is_none() {
                    actual_start = Some(frames.len());
                }
                true
            });
        }

        // If no frames came out assume that this is an unsupported platform
        // since `backtrace` doesn't provide a way of learning this right now,
        // and this should be a good enough approximation.
        let inner = if frames.len() == 0 {
            Inner::Unsupported
        } else {
            Inner::Captured(Mutex::new(Capture {
                actual_start: actual_start.unwrap_or(0),
                frames,
                resolved: false,
            }))
        };

        Backtrace { inner }
    }

    /// Returns the status of this backtrace, indicating whether this backtrace
    /// request was unsupported, disabled, or a stack trace was actually
    /// captured.
    pub fn status(&self) -> BacktraceStatus {
        match self.inner {
            Inner::Unsupported => BacktraceStatus::Unsupported,
            Inner::Disabled => BacktraceStatus::Disabled,
            Inner::Captured(_) => BacktraceStatus::Captured,
        }
    }
}

impl fmt::Display for Backtrace {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut capture = match &self.inner {
            Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
            Inner::Disabled => return fmt.write_str("disabled backtrace"),
            Inner::Captured(c) => c.lock().unwrap(),
        };
        capture.resolve();

        let full = fmt.alternate();
        let (frames, style) = if full {
            (&capture.frames[..], backtrace::PrintFmt::Full)
        } else {
            (&capture.frames[capture.actual_start..], backtrace::PrintFmt::Short)
        };

        // When printing paths we try to strip the cwd if it exists, otherwise
        // we just print the path as-is. Note that we also only do this for the
        // short format, because if it's full we presumably want to print
        // everything.
        let cwd = crate::env::current_dir();
        let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
            output_filename(fmt, path, style, cwd.as_ref().ok())
        };

        let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path);
        f.add_context()?;
        for frame in frames {
            let mut f = f.frame();
            if frame.symbols.is_empty() {
                f.print_raw(frame.frame.ip(), None, None, None)?;
            } else {
                for symbol in frame.symbols.iter() {
                    f.print_raw(
                        frame.frame.ip(),
                        symbol.name.as_ref().map(|b| backtrace::SymbolName::new(b)),
                        symbol.filename.as_ref().map(|b| match b {
                            BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
                            BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
                        }),
                        symbol.lineno,
                    )?;
                }
            }
        }
        f.finish()?;
        Ok(())
    }
}

impl Capture {
    fn resolve(&mut self) {
        // If we're already resolved, nothing to do!
        if self.resolved {
            return;
        }
        self.resolved = true;

        // Use the global backtrace lock to synchronize this as it's a
        // requirement of the `backtrace` crate, and then actually resolve
        // everything.
        let _lock = lock();
        for frame in self.frames.iter_mut() {
            let symbols = &mut frame.symbols;
            unsafe {
                backtrace::resolve_frame_unsynchronized(&frame.frame, |symbol| {
                    symbols.push(BacktraceSymbol {
                        name: symbol.name().map(|m| m.as_bytes().to_vec()),
                        filename: symbol.filename_raw().map(|b| match b {
                            BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
                            BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
                        }),
                        lineno: symbol.lineno(),
                    });
                });
            }
        }
    }
}

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

    #[test]
    fn debug_backtrace_fmt() {
        let bt = Backtrace::capture();
        eprintln!("uncaptured: {:?}", bt);
        let bt = Backtrace::force_capture();
        eprintln!("captured: {:?}", bt);
        eprintln!("display print: {}", bt);
        eprintln!("display print alt: {:#}", bt);
        eprintln!("resolved: {:?}", bt);
        eprintln!("resolved alt: {:#?}", bt);
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::iter;

use adivon::bag::Bag;

/// Capacitated edge with a flow in a flow network.
#[derive(Clone, Copy)]
pub struct FlowEdge {
    v: usize,                   // from
    w: usize,                   // to
    capacity: f64,
    flow: f64
}

impl FlowEdge {
    pub fn new_zero_flow(v: usize, w: usize, capacity: f64) -> FlowEdge {
        assert!(capacity >= 0.0, "edge capacity must be non-negative");
        FlowEdge {
            v: v,
            w: w,
            capacity: capacity,
            flow: 0.0
        }
    }

    pub fn new(v: usize, w: usize, capacity: f64, flow: f64) -> FlowEdge {
        assert!(capacity >= 0.0, "edge capacity must be non-negative");
        assert!(flow <= capacity, "flow exceeds capacity");
        assert!(flow >= 0.0, "flow must be non-negative");
        FlowEdge {
            v: v,
            w: w,
            capacity: capacity,
            flow: flow
        }
    }

    pub fn from(&self) -> usize {
        self.v
    }

    pub fn to(&self) -> usize {
        self.w
    }

    pub fn capacity(&self) -> f64 {
        self.capacity
    }

    pub fn flow(&self) -> f64 {
        self.flow
    }

    pub fn other(&self, vertex: usize) -> usize {
        if vertex == self.v {
            self.w
        } else if vertex == self.w {
            self.v
        } else {
            panic!("illegal endpoint")
        }
    }

    pub fn residual_capacity_to(&self, vertex: usize) -> f64 {
        if vertex == self.v {
            self.flow
        } else if vertex == self.w {
            self.capacity - self.flow
        } else {
            panic!("illegal endpoint")
        }
    }

    pub fn add_residual_flow_to(&mut self, vertex: usize, delta: f64) {
        assert!(!delta.is_nan(), "change in flow = NaN");
        if vertex == self.v {
            self.flow -= delta;
        } else if vertex == self.w {
            self.flow += delta;
        } else {
            panic!("illegal endpoint")
        }
        assert!(self.flow >= 0.0 && self.flow <= self.capacity)
    }
}

impl fmt::Debug for FlowEdge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {} {}/{}", self.v, self.w, self.flow, self.capacity)
    }
}

#[test]
fn test_flow_edge() {
    let e = FlowEdge::new_zero_flow(12, 23, 3.14);
    assert_eq!("12 -> 23 0/3.14", format!("{:?}", e));
}

/// A capacitated flow network, implemented using adjacency lists.
pub struct FlowNetwork {
    v: usize,
    e: usize,
    adj: Vec<Bag<FlowEdge>>
}

impl FlowNetwork {
    pub fn new(v: usize) -> FlowNetwork {
        FlowNetwork {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::new()).take(v).collect()
        }
    }

    pub fn v(&self) -> usize {
        self.v
    }

    pub fn e(&self) -> usize {
        self.e
    }

    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v())
    }

    pub fn add_edge(&mut self, e: FlowEdge) {
        let v = e.from();
        let w = e.to();
        self.validate_vertex(v);
        self.validate_vertex(w);
        self.adj[v].add(e);
        self.adj[w].add(e);
        self.e += 1;
    }

    pub fn adj(&self, v: usize) -> ::std::vec::IntoIter<FlowEdge> {
        self.validate_vertex(v);
        self.adj[v].iter().map(|e| e.clone()).collect::<Vec<FlowEdge>>().into_iter()
    }

    pub fn edges(&self) -> ::std::vec::IntoIter<FlowEdge> {
        self.adj.iter()
            .flat_map(|adj| {
                adj.iter().map(|e| e.clone()).collect::<Vec<FlowEdge>>().into_iter()
            })
            .collect::<Vec<FlowEdge>>()
            .into_iter()
    }
}
<commit_msg>maximum_flow: implement Ford-Fulkerson algorithm for FlowNetwork<commit_after>use std::fmt;
use std::iter;
use std::f64;
use std::sync::{Arc, RwLock};

use adivon::bag::Bag;
use adivon::queue::Queue;

/// Capacitated edge with a flow in a flow network.
pub struct FlowEdge {
    v: usize,                   // from
    w: usize,                   // to
    capacity: f64,
    flow: f64
}

impl FlowEdge {
    pub fn new(v: usize, w: usize, capacity: f64) -> FlowEdge {
        assert!(capacity >= 0.0, "edge capacity must be non-negative");
        FlowEdge {
            v: v,
            w: w,
            capacity: capacity,
            flow: 0.0
        }
    }

    pub fn new_with_flow(v: usize, w: usize, capacity: f64, flow: f64) -> FlowEdge {
        assert!(capacity >= 0.0, "edge capacity must be non-negative");
        assert!(flow <= capacity, "flow exceeds capacity");
        assert!(flow >= 0.0, "flow must be non-negative");
        FlowEdge {
            v: v,
            w: w,
            capacity: capacity,
            flow: flow
        }
    }

    pub fn from(&self) -> usize {
        self.v
    }

    pub fn to(&self) -> usize {
        self.w
    }

    pub fn capacity(&self) -> f64 {
        self.capacity
    }

    pub fn flow(&self) -> f64 {
        self.flow
    }

    pub fn other(&self, vertex: usize) -> usize {
        if vertex == self.v {
            self.w
        } else if vertex == self.w {
            self.v
        } else {
            panic!("illegal endpoint")
        }
    }

    pub fn residual_capacity_to(&self, vertex: usize) -> f64 {
        if vertex == self.v {
            self.flow
        } else if vertex == self.w {
            self.capacity - self.flow
        } else {
            panic!("illegal endpoint")
        }
    }

    pub fn add_residual_flow_to(&mut self, vertex: usize, delta: f64) {
        assert!(!delta.is_nan(), "change in flow = NaN");
        if vertex == self.v {
            self.flow -= delta;
        } else if vertex == self.w {
            self.flow += delta;
        } else {
            panic!("illegal endpoint")
        }
        assert!(self.flow >= 0.0 && self.flow <= self.capacity)
    }
}

impl fmt::Debug for FlowEdge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {} {}/{}", self.v, self.w, self.flow, self.capacity)
    }
}

#[test]
fn test_flow_edge() {
    let e = FlowEdge::new(12, 23, 3.14);
    assert_eq!("12 -> 23 0/3.14", format!("{:?}", e));
}

/// A capacitated flow network, implemented using adjacency lists.
pub struct FlowNetwork {
    v: usize,
    e: usize,
    adj: Vec<Bag<Arc<RwLock<FlowEdge>>>>
}

impl FlowNetwork {
    pub fn new(v: usize) -> FlowNetwork {
        FlowNetwork {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::new()).take(v).collect()
        }
    }

    pub fn v(&self) -> usize {
        self.v
    }

    pub fn e(&self) -> usize {
        self.e
    }

    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v())
    }

    pub fn add_edge(&mut self, e: FlowEdge) {
        let v = e.from();
        let w = e.to();
        self.validate_vertex(v);
        self.validate_vertex(w);
        let edge = Arc::new(RwLock::new(e));
        self.adj[v].add(edge.clone());
        self.adj[w].add(edge);
        self.e += 1;
    }

    pub fn adj<'r>(&'r self, v: usize) -> Vec<Arc<RwLock<FlowEdge>>> {
        self.validate_vertex(v);
        self.adj[v].iter().map(|e| e.clone()).collect()
    }

    pub fn edges<'r>(&'r self) -> Vec<Arc<RwLock<FlowEdge>>> {
        self.adj.iter()
            .flat_map(|adj| {
                adj.iter()
                    .map(|e| e.clone())
                    .collect::<Vec<Arc<RwLock<FlowEdge>>>>()
                    .into_iter()
            })
            .collect()
    }

    pub fn to_dot(&self) -> String {
        let mut dot = String::new();

        dot.push_str("digraph G {\n");
        for i in 0 .. self.v {
            dot.push_str(&format!("  {};\n", i));
        }

        for v in 0 .. self.v() {
            for e in self.adj(v) {
                let e = e.read().unwrap();
                if v == e.from() {
                    let w = e.to();
                    dot.push_str(&format!("  {} -> {} [ label=\"{}/{}\" ];\n",
                                          v, w, e.flow, e.capacity));
                }
            }
        }
        dot.push_str("}\n");
        dot
    }
}

/// Ford-Fulkerson algorithm for computing a max flow and
/// a min cut using shortest augmenting path rule.
pub struct FordFulkerson<'g> {
    graph: &'g FlowNetwork,
    marked: Vec<bool>,
    edge_to: Vec<Option<Arc<RwLock<FlowEdge>>>>,
    value: f64
}

impl<'g> FordFulkerson<'g> {
    fn new<'a>(graph: &'a FlowNetwork, s: usize, t: usize) -> FordFulkerson<'a> {
        graph.validate_vertex(s);
        graph.validate_vertex(t);

        let n = graph.v();
        let mut ret = FordFulkerson {
            graph: graph,
            marked: iter::repeat(false).take(n).collect(),
            edge_to: iter::repeat(None).take(n).collect(),
            value: 0.0
        };
        ret.ford_fulkerson(s, t);
        ret
    }

    fn ford_fulkerson(&mut self, s: usize, t: usize) {
        // if !self.is_feasible(s, t) {
        //     panic!("initial flow is infeasible");
        // }

        self.value = self.excess(t);
        while self.has_augmenting_path(s, t) {
            let mut bottle = f64::INFINITY;
            let mut v = t;
            while v != s {
                let ee = self.edge_to[v].as_ref().unwrap().clone();
                bottle = bottle.min(ee.write().unwrap().residual_capacity_to(v));
                v = ee.read().unwrap().other(v);
            }

            // augment flow
            let mut v = t;
            while v != s {
                let ee = self.edge_to[v].as_ref().unwrap().clone();
                ee.write().unwrap().add_residual_flow_to(v, bottle);
                v = ee.read().unwrap().other(v);
            }

            self.value += bottle;
            println!("233 value=> {}", self.value);
        }

    }

    pub fn value(&self) -> f64 {
        self.value
    }

    // is v in the s side of the min s-t cut?
    pub fn in_cut(&self, v: usize) -> bool {
        self.graph.validate_vertex(v);
        self.marked[v]
    }

    // is there an augmenting path?
    fn has_augmenting_path(&mut self, s: usize, t: usize) -> bool {
        println!("calling s={} t={}", s, t);
        self.edge_to = iter::repeat(None).take(self.graph.v()).collect();
        self.marked = iter::repeat(false).take(self.graph.v()).collect();

        let mut queue = Queue::<usize>::new();
        queue.enqueue(s);
        self.marked[s] = true;
        while !queue.is_empty() && !self.marked[t] {
            let v = queue.dequeue().unwrap();

            for ee in self.graph.adj(v) {
                let e = ee.read().unwrap();
                let w = e.other(v);

                // if residual capacity from v to w
                if e.residual_capacity_to(w) > 0.0 {
                    if !self.marked[w] {
                        self.edge_to[w] = Some(ee.clone());
                        self.marked[w] = true;
                        queue.enqueue(w);
                    }
                }
            }
        }
        self.marked[t]
    }

    // return excess flow at vertex v
    fn excess(&self, v: usize) -> f64 {
        let mut excess = 0f64;
        for e in self.graph.adj(v) {
            let e = e.read().unwrap();
            if v == e.from() {
                excess -= e.flow();
            } else {
                excess += e.flow();
            }
        }
        excess
    }

    fn is_feasible(&self, s: usize, t: usize) -> bool {
        const EPSILON: f64 = 1e-11;

        // check that capacity constraints are satisfied
        for v in 0 .. self.graph.v() {
            for e in self.graph.adj(v) {
                let e = e.read().unwrap();
                if e.flow() < -EPSILON || e.flow() > e.capacity() + EPSILON {
                    panic!("edge does not satisfy capacity constraints");
                    // return false;
                }
            }
        }

        // check that net flow into a vertex equals zero, except at source and sink
        if (self.value + self.excess(s)).abs() > EPSILON {
            panic!("Excess at source = {}", self.excess(s));
            // return false;
        }

        for v in 0 .. self.graph.v() {
            if v == s || v == t {
                continue;
            } else {
                if self.excess(v).abs() > EPSILON {
                    panic!("net flow out of {:?} doesn't equal zero", v);
                    // return false;
                }
            }
        }
        return true;
    }
}

impl FlowNetwork {
    pub fn ford_fulkerson<'a>(&'a self, s: usize, t: usize) -> FordFulkerson<'a> {
        assert!(s != t, "source equal to sink");
        FordFulkerson::new(self, s, t)
    }
}

#[test]
fn test_flow_network() {
    let mut g = FlowNetwork::new(6);

    g.add_edge(FlowEdge::new(0, 1, 2.0));
    g.add_edge(FlowEdge::new(0, 2, 3.0));
    g.add_edge(FlowEdge::new(1, 3, 3.0));
    g.add_edge(FlowEdge::new(1, 4, 1.0));
    g.add_edge(FlowEdge::new(2, 3, 1.0));
    g.add_edge(FlowEdge::new(2, 4, 1.0));
    g.add_edge(FlowEdge::new(3, 5, 2.0));
    g.add_edge(FlowEdge::new(4, 3, 3.0));

    println!("=> \n{}", g.to_dot());

    let maxflow = g.ford_fulkerson(0, 5);

}
<|endoftext|>


<commit_before>use std::path;
use std::process;

use rust_sodium;

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

use config;
use models;
use web;
use utils;

pub fn main() {
    let username_arg = Arg::with_name("USERNAME")
        .help("The username to perform the operation with.")
        .required(true)
        .index(1);

    let matches =
        App::new("mysteryshack")
        .version(env!("CARGO_PKG_VERSION"))
        .author("Markus Unterwaditzer & contributors")
        .about("A remoteStorage server.")
        .args_from_usage("-c, --config=[FILE] 'Use specified config file, defaults to ./config'")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("serve")
                    .about("Start server"))
        .subcommand(SubCommand::with_name("user")
                    .about("User management")
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("create")
                                .about("Create a new user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("setpass")
                                .about("Change password for user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("delete")
                                .about("Delete a user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("authorize")
                                .about("Create a OAuth token. This is mostly useful for development.")
                                .arg(username_arg.clone())
                                .arg(Arg::with_name("CLIENT_ID").required(true).index(2))
                                .arg(Arg::with_name("SCOPE").required(true).index(3))))
        .get_matches();

    assert!(rust_sodium::init());

    let config_path = path::Path::new(matches.value_of("config").unwrap_or("./config"));

    let config = match config::Config::read_file(config_path) {
        Ok(x) => x,
        Err(e) => {
            println!("Failed to parse config: {}", e);
            process::exit(1);
        }
    };

    clap_dispatch!(matches; {
        serve() => web::run_server(config),
        user(user_matches) => clap_dispatch!(user_matches; {
            create(_, USERNAME as username) => {
                let password_hash = models::PasswordHash::from_password(
                    utils::double_password_prompt("Password for new user: ").unwrap_or_else(|| process::exit(1)));

                match models::User::create(&config.data_path, username).map(|user| {
                    user.set_password_hash(password_hash)
                }) {
                    Ok(_) => (),
                    Err(e) => {
                        println!("Failed to create user {}: {}", username, e);
                        process::exit(1);
                    }
                };

                println!("Successfully created user {}", username);
            },
            setpass(_, USERNAME as username) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                let password_hash = models::PasswordHash::from_password(
                    utils::double_password_prompt("New password: ").unwrap_or_else(|| process::exit(1)));
                match user.set_password_hash(password_hash) {
                    Ok(_) => (),
                    Err(e) => {
                        println!("Failed to set password for user {}: {}", username, e);
                        process::exit(1);
                    }
                };

                println!("Changed password for user {}", username);
            },
            delete(_, USERNAME as username) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                println!("You are about to delete the user {} and ALL the user's user data. This
                         process is irreversible.", username);
                if !utils::prompt_confirm(format!("Do you want to delete the user {}?", username), false) {
                    println!("Aborted!");
                    process::exit(1);
                }

                match user.delete() {
                    Ok(_) => println!("Successfully deleted user {}.", username),
                    Err(e) => {
                        println!("Failed to delete user: {:?}", e);
                        process::exit(1);
                    }
                };
            },
            authorize(_, USERNAME as username, SCOPE as scope, CLIENT_ID as client_id) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                let oauth_session = web::oauth::Session {
                    client_id: client_id.to_owned(),
                    permissions: match web::oauth::PermissionsMap::from_scope_string(scope) {
                        Some(x) => x,
                        None => {
                            println!("Invalid scope: {}", scope);
                            process::exit(1);
                        }
                    }
                };

                let (_, token) = models::Token::create(&user, oauth_session).unwrap();
                println!("{}", token.token(&user));
            }
        })
    });
}
<commit_msg>Check if user exists before prompting for password<commit_after>use std::path;
use std::process;

use rust_sodium;

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

use config;
use models;
use web;
use utils;

pub fn main() {
    let username_arg = Arg::with_name("USERNAME")
        .help("The username to perform the operation with.")
        .required(true)
        .index(1);

    let matches =
        App::new("mysteryshack")
        .version(env!("CARGO_PKG_VERSION"))
        .author("Markus Unterwaditzer & contributors")
        .about("A remoteStorage server.")
        .args_from_usage("-c, --config=[FILE] 'Use specified config file, defaults to ./config'")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("serve")
                    .about("Start server"))
        .subcommand(SubCommand::with_name("user")
                    .about("User management")
                    .setting(AppSettings::SubcommandRequired)
                    .subcommand(SubCommand::with_name("create")
                                .about("Create a new user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("setpass")
                                .about("Change password for user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("delete")
                                .about("Delete a user")
                                .arg(username_arg.clone()))
                    .subcommand(SubCommand::with_name("authorize")
                                .about("Create a OAuth token. This is mostly useful for development.")
                                .arg(username_arg.clone())
                                .arg(Arg::with_name("CLIENT_ID").required(true).index(2))
                                .arg(Arg::with_name("SCOPE").required(true).index(3))))
        .get_matches();

    assert!(rust_sodium::init());

    let config_path = path::Path::new(matches.value_of("config").unwrap_or("./config"));

    let config = match config::Config::read_file(config_path) {
        Ok(x) => x,
        Err(e) => {
            println!("Failed to parse config: {}", e);
            process::exit(1);
        }
    };

    clap_dispatch!(matches; {
        serve() => web::run_server(config),
        user(user_matches) => clap_dispatch!(user_matches; {
            create(_, USERNAME as username) => {
                if models::User::get(&config.data_path, username).is_some() {
                    println!("User already exists. Please delete the user first.");
                    process::exit(1);
                }

                let password_hash = models::PasswordHash::from_password(
                    utils::double_password_prompt("Password for new user: ").unwrap_or_else(|| process::exit(1)));

                match models::User::create(&config.data_path, username).map(|user| {
                    user.set_password_hash(password_hash)
                }) {
                    Ok(_) => (),
                    Err(e) => {
                        println!("Failed to create user {}: {}", username, e);
                        process::exit(1);
                    }
                };

                println!("Successfully created user {}", username);
            },
            setpass(_, USERNAME as username) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                let password_hash = models::PasswordHash::from_password(
                    utils::double_password_prompt("New password: ").unwrap_or_else(|| process::exit(1)));
                match user.set_password_hash(password_hash) {
                    Ok(_) => (),
                    Err(e) => {
                        println!("Failed to set password for user {}: {}", username, e);
                        process::exit(1);
                    }
                };

                println!("Changed password for user {}", username);
            },
            delete(_, USERNAME as username) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                println!("You are about to delete the user {} and ALL the user's user data. This
                         process is irreversible.", username);
                if !utils::prompt_confirm(format!("Do you want to delete the user {}?", username), false) {
                    println!("Aborted!");
                    process::exit(1);
                }

                match user.delete() {
                    Ok(_) => println!("Successfully deleted user {}.", username),
                    Err(e) => {
                        println!("Failed to delete user: {:?}", e);
                        process::exit(1);
                    }
                };
            },
            authorize(_, USERNAME as username, SCOPE as scope, CLIENT_ID as client_id) => {
                let user = match models::User::get(&config.data_path, username) {
                    Some(x) => x,
                    None => {
                        println!("User does not exist: {}", username);
                        process::exit(1);
                    }
                };

                let oauth_session = web::oauth::Session {
                    client_id: client_id.to_owned(),
                    permissions: match web::oauth::PermissionsMap::from_scope_string(scope) {
                        Some(x) => x,
                        None => {
                            println!("Invalid scope: {}", scope);
                            process::exit(1);
                        }
                    }
                };

                let (_, token) = models::Token::create(&user, oauth_session).unwrap();
                println!("{}", token.token(&user));
            }
        })
    });
}
<|endoftext|>


<commit_before>//! The element nodes.

use std::fmt;

use node::{Attributes, Children, Node, Value};

/// An element node.
pub struct Element {
    name: String,
    attributes: Attributes,
    children: Children,
}

impl Element {
    /// Create a node.
    #[inline]
    pub fn new<T>(name: T) -> Self where T: Into<String> {
        Element { name: name.into(), attributes: Attributes::new(), children: Children::new() }
    }
}

impl fmt::Display for Element {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(formatter, "<{}", self.name));
        let mut attributes = self.attributes.iter().collect::<Vec<_>>();
        attributes.sort_by_key(|pair| pair.0.as_str());
        for (name, value) in attributes {
            try!(write!(formatter, " {}='{}'", name, value));
        }
        if self.children.is_empty() {
            return write!(formatter, "/>");
        }
        try!(write!(formatter, ">"));
        for child in self.children.iter() {
            try!(write!(formatter, "\n{}", child));
        }
        write!(formatter, "\n</{}>", self.name)
    }
}

impl Node for Element {
    #[inline]
    fn append<T>(&mut self, node: T) where T: Node {
        self.children.push(Box::new(node));
    }

    #[inline]
    fn assign<T, U>(&mut self, name: T, value: U) where T: Into<String>, U: Into<Value> {
        self.attributes.insert(name.into(), value.into());
    }
}

/// A [`svg`][1] element.
///
/// [1]: https://www.w3.org/TR/SVG/struct.html#SVGElement
pub struct SVG {
    inner: Element,
}

impl SVG {
    /// Create a node.
    #[inline]
    pub fn new() -> Self {
        let mut inner = Element::new("svg");
        inner.assign("xmlns", "http://www.w3.org/2000/svg");
        SVG { inner: inner }
    }
}

node! { SVG::inner }

macro_rules! implement {
    ($(#[$doc:meta] struct $struct_name:ident($tag_name:expr))*) => (
        $(
            #[$doc]
            pub struct $struct_name {
                inner: ::node::element::Element,
            }

            impl $struct_name {
                /// Create a node.
                #[inline]
                pub fn new() -> Self {
                    $struct_name { inner: Element::new($tag_name) }
                }
            }

            node! { $struct_name::inner }
        )*
    );
}

implement! {
    #[doc = "An [`animate`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateElement"]
    struct Animate("animate")

    #[doc = "An [`animateColor`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateColorElement"]
    struct AnimateColor("animateColor")

    #[doc = "An [`animateMotion`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateMotionElement"]
    struct AnimateMotion("animateMotion")

    #[doc = "An [`animateTransform`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateTransformElement"]
    struct AnimateTransform("animateTransform")

    #[doc = "A [`circle`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#CircleElement"]
    struct Circle("circle")

    #[doc = "A [`clipPath`][1] element.
    [1]: https://www.w3.org/TR/SVG/masking.html#ClipPathElement"]
    struct ClipPath("clipPath")

    #[doc = "A [`defs`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#DefsElement"]
    struct Definitions("defs")

    #[doc = "A [`desc`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#DescElement"]
    struct Description("desc")

    #[doc = "An [`ellipse`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#EllipseElement"]
    struct Ellipse("ellipse")

    #[doc = "A [`filter`][1] element.
    [1]: https://www.w3.org/TR/SVG/filters.html#FilterElement"]
    struct Filter("filter")

    #[doc = "A [`g`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#GElement"]
    struct Group("g")

    #[doc = "An [`image`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#ImageElement"]
    struct Image("image")

    #[doc = "A [`line`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#LineElement"]
    struct Line("line")

    #[doc = "A [`linearGradient`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#LinearGradientElement"]
    struct LinearGradient("linearGradient")

    #[doc = "An [`a`][1] element.
    [1]: https://www.w3.org/TR/SVG/linking.html#AElement"]
    struct Link("a")

    #[doc = "A [`marker`][1] element.
    [1]: https://www.w3.org/TR/SVG/painting.html#MarkerElement"]
    struct Marker("marker")

    #[doc = "A [`mask`][1] element.
    [1]: https://www.w3.org/TR/SVG/masking.html#MaskElement"]
    struct Mask("mask")

    #[doc = "An [`mpath`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#MPathElement"]
    struct MotionPath("mpath")

    #[doc = "A [`path`][1] element.
    [1]: https://www.w3.org/TR/SVG/paths.html#PathElement"]
    struct Path("path")

    #[doc = "A [`polygon`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#PolygonElement"]
    struct Polygon("polygon")

    #[doc = "A [`polyline`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#PolylineElement"]
    struct Polyline("polyline")

    #[doc = "A [`radialGradient`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#RadialGradientElement"]
    struct RadialGradient("radialGradient")

    #[doc = "A [`rect`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#RectElement"]
    struct Rectangle("rect")

    #[doc = "A [`script`][1] element.
    [1]: https://www.w3.org/TR/SVG/script.html#ScriptElement"]
    struct Script("script")

    #[doc = "A [`stop`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#StopElement"]
    struct Stop("stop")

    #[doc = "A [`style`][1] element.
    [1]: https://www.w3.org/TR/SVG/styling.html#StyleElement"]
    struct Style("style")

    #[doc = "A [`symbol`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#SymbolElement"]
    struct Symbol("symbol")

    #[doc = "A [`text`][1] element.
    [1]: https://www.w3.org/TR/SVG/text.html#TextElement"]
    struct Text("text")

    #[doc = "A [`textPath`][1] element.
    [1]: https://www.w3.org/TR/SVG/text.html#TextPathElement"]
    struct TextPath("textPath")

    #[doc = "A [`title`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#TitleElement"]
    struct Title("title")

    #[doc = "A [`use`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#UseElement"]
    struct Use("use")
}

pub mod path;

#[cfg(test)]
mod tests {
    use node::Node;
    use super::Element;

    #[test]
    fn display() {
        let mut element = Element::new("foo");
        element.assign("x", -15);
        element.assign("y", "10px");
        element.assign("size", (42.5, 69.0));
        element.assign("color", "green");
        element.append(Element::new("bar"));
        assert_eq!(element.to_string(), "\
            <foo color='green' size='42.5 69' x='-15' y='10px'>\n\
            <bar/>\n\
            </foo>\
        ");
    }
}
<commit_msg>Make a cosmetic adjustment<commit_after>//! The element nodes.

use std::fmt;

use node::{Attributes, Children, Node, Value};

/// An element node.
pub struct Element {
    name: String,
    attributes: Attributes,
    children: Children,
}

impl Element {
    /// Create a node.
    #[inline]
    pub fn new<T>(name: T) -> Self where T: Into<String> {
        Element { name: name.into(), attributes: Attributes::new(), children: Children::new() }
    }
}

impl fmt::Display for Element {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(formatter, "<{}", self.name));
        let mut attributes = self.attributes.iter().collect::<Vec<_>>();
        attributes.sort_by_key(|pair| pair.0.as_str());
        for (name, value) in attributes {
            try!(write!(formatter, " {}='{}'", name, value));
        }
        if self.children.is_empty() {
            return write!(formatter, "/>");
        }
        try!(write!(formatter, ">"));
        for child in self.children.iter() {
            try!(write!(formatter, "\n{}", child));
        }
        write!(formatter, "\n</{}>", self.name)
    }
}

impl Node for Element {
    #[inline]
    fn append<T>(&mut self, node: T) where T: Node {
        self.children.push(Box::new(node));
    }

    #[inline]
    fn assign<T, U>(&mut self, name: T, value: U) where T: Into<String>, U: Into<Value> {
        self.attributes.insert(name.into(), value.into());
    }
}

/// A [`svg`][1] element.
///
/// [1]: https://www.w3.org/TR/SVG/struct.html#SVGElement
pub struct SVG {
    inner: Element,
}

impl SVG {
    /// Create a node.
    #[inline]
    pub fn new() -> Self {
        let mut inner = Element::new("svg");
        inner.assign("xmlns", "http://www.w3.org/2000/svg");
        SVG { inner: inner }
    }
}

node! { SVG::inner }

macro_rules! implement {
    ($(#[$doc:meta] struct $struct_name:ident($tag_name:expr))*) => (
        $(
            #[$doc]
            pub struct $struct_name {
                inner: Element,
            }

            impl $struct_name {
                /// Create a node.
                #[inline]
                pub fn new() -> Self {
                    $struct_name { inner: Element::new($tag_name) }
                }
            }

            node! { $struct_name::inner }
        )*
    );
}

implement! {
    #[doc = "An [`animate`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateElement"]
    struct Animate("animate")

    #[doc = "An [`animateColor`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateColorElement"]
    struct AnimateColor("animateColor")

    #[doc = "An [`animateMotion`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateMotionElement"]
    struct AnimateMotion("animateMotion")

    #[doc = "An [`animateTransform`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#AnimateTransformElement"]
    struct AnimateTransform("animateTransform")

    #[doc = "A [`circle`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#CircleElement"]
    struct Circle("circle")

    #[doc = "A [`clipPath`][1] element.
    [1]: https://www.w3.org/TR/SVG/masking.html#ClipPathElement"]
    struct ClipPath("clipPath")

    #[doc = "A [`defs`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#DefsElement"]
    struct Definitions("defs")

    #[doc = "A [`desc`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#DescElement"]
    struct Description("desc")

    #[doc = "An [`ellipse`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#EllipseElement"]
    struct Ellipse("ellipse")

    #[doc = "A [`filter`][1] element.
    [1]: https://www.w3.org/TR/SVG/filters.html#FilterElement"]
    struct Filter("filter")

    #[doc = "A [`g`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#GElement"]
    struct Group("g")

    #[doc = "An [`image`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#ImageElement"]
    struct Image("image")

    #[doc = "A [`line`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#LineElement"]
    struct Line("line")

    #[doc = "A [`linearGradient`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#LinearGradientElement"]
    struct LinearGradient("linearGradient")

    #[doc = "An [`a`][1] element.
    [1]: https://www.w3.org/TR/SVG/linking.html#AElement"]
    struct Link("a")

    #[doc = "A [`marker`][1] element.
    [1]: https://www.w3.org/TR/SVG/painting.html#MarkerElement"]
    struct Marker("marker")

    #[doc = "A [`mask`][1] element.
    [1]: https://www.w3.org/TR/SVG/masking.html#MaskElement"]
    struct Mask("mask")

    #[doc = "An [`mpath`][1] element.
    [1]: https://www.w3.org/TR/SVG/animate.html#MPathElement"]
    struct MotionPath("mpath")

    #[doc = "A [`path`][1] element.
    [1]: https://www.w3.org/TR/SVG/paths.html#PathElement"]
    struct Path("path")

    #[doc = "A [`polygon`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#PolygonElement"]
    struct Polygon("polygon")

    #[doc = "A [`polyline`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#PolylineElement"]
    struct Polyline("polyline")

    #[doc = "A [`radialGradient`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#RadialGradientElement"]
    struct RadialGradient("radialGradient")

    #[doc = "A [`rect`][1] element.
    [1]: https://www.w3.org/TR/SVG/shapes.html#RectElement"]
    struct Rectangle("rect")

    #[doc = "A [`script`][1] element.
    [1]: https://www.w3.org/TR/SVG/script.html#ScriptElement"]
    struct Script("script")

    #[doc = "A [`stop`][1] element.
    [1]: https://www.w3.org/TR/SVG/pservers.html#StopElement"]
    struct Stop("stop")

    #[doc = "A [`style`][1] element.
    [1]: https://www.w3.org/TR/SVG/styling.html#StyleElement"]
    struct Style("style")

    #[doc = "A [`symbol`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#SymbolElement"]
    struct Symbol("symbol")

    #[doc = "A [`text`][1] element.
    [1]: https://www.w3.org/TR/SVG/text.html#TextElement"]
    struct Text("text")

    #[doc = "A [`textPath`][1] element.
    [1]: https://www.w3.org/TR/SVG/text.html#TextPathElement"]
    struct TextPath("textPath")

    #[doc = "A [`title`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#TitleElement"]
    struct Title("title")

    #[doc = "A [`use`][1] element.
    [1]: https://www.w3.org/TR/SVG/struct.html#UseElement"]
    struct Use("use")
}

pub mod path;

#[cfg(test)]
mod tests {
    use node::Node;
    use super::Element;

    #[test]
    fn display() {
        let mut element = Element::new("foo");
        element.assign("x", -15);
        element.assign("y", "10px");
        element.assign("size", (42.5, 69.0));
        element.assign("color", "green");
        element.append(Element::new("bar"));
        assert_eq!(element.to_string(), "\
            <foo color='green' size='42.5 69' x='-15' y='10px'>\n\
            <bar/>\n\
            </foo>\
        ");
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};
use info::filetype::FileExtensions;
use output::Colours;
use output::escape;
use output::cell::TextCellContents;

/// Basically a file name factory.
#[derive(Debug)]
pub struct FileStyle {

    /// Whether to append file class characters to file names.
    pub classify: Classify,

    /// Mapping of file extensions to colours, to highlight regular files.
    pub exts: FileExtensions,
}

impl FileStyle {

    /// Create a new `FileName` that prints the given file’s name, painting it
    /// with the remaining arguments.
    pub fn for_file<'a, 'dir>(&'a self, file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
        FileName {
            file, colours,
            link_style: LinkStyle::JustFilenames,
            exts:       &self.exts,
            classify:   self.classify,
            target:     if file.is_link() { Some(file.link_target()) }
                                     else { None }
        }
    }
}

/// When displaying a file name, there needs to be some way to handle broken
/// links, depending on how long the resulting Cell can be.
#[derive(PartialEq, Debug, Copy, Clone)]
enum LinkStyle {

    /// Just display the file names, but colour them differently if they’re
    /// a broken link or can’t be followed.
    JustFilenames,

    /// Display all files in their usual style, but follow each link with an
    /// arrow pointing to their path, colouring the path differently if it’s
    /// a broken link, and doing nothing if it can’t be followed.
    FullLinkPaths,
}

/// Whether to append file class characters to the file names.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum Classify {

    /// Just display the file names, without any characters.
    JustFilenames,

    /// Add a character after the file name depending on what class of file
    /// it is.
    AddFileIndicators,
}

impl Default for Classify {
    fn default() -> Classify {
        Classify::JustFilenames
    }
}

/// A **file name** holds all the information necessary to display the name
/// of the given file. This is used in all of the views.
pub struct FileName<'a, 'dir: 'a> {

    /// A reference to the file that we're getting the name of.
    file: &'a File<'dir>,

    /// The colours used to paint the file name and its surrounding text.
    colours: &'a Colours,

    /// The file that this file points to if it's a link.
    target: Option<FileTarget<'dir>>,

    /// How to handle displaying links.
    link_style: LinkStyle,

    /// Whether to append file class characters to file names.
    classify: Classify,

    /// Mapping of file extensions to colours, to highlight regular files.
    exts: &'a FileExtensions,
}

impl<'a, 'dir> FileName<'a, 'dir> {

    /// Sets the flag on this file name to display link targets with an
    /// arrow followed by their path.
    pub fn with_link_paths(mut self) -> Self {
        self.link_style = LinkStyle::FullLinkPaths;
        self
    }

    /// Paints the name of the file using the colours, resulting in a vector
    /// of coloured cells that can be printed to the terminal.
    ///
    /// This method returns some `TextCellContents`, rather than a `TextCell`,
    /// because for the last cell in a table, it doesn’t need to have its
    /// width calculated.
    pub fn paint(&self) -> TextCellContents {
        let mut bits = Vec::new();

        if self.file.parent_dir.is_none() {
            if let Some(parent) = self.file.path.parent() {
                self.add_parent_bits(&mut bits, parent);
            }
        }

        if !self.file.name.is_empty() {
            for bit in self.coloured_file_name() {
                bits.push(bit);
            }
        }

        if let (LinkStyle::FullLinkPaths, Some(target)) = (self.link_style, self.target.as_ref()) {
            match *target {
                FileTarget::Ok(ref target) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.punctuation.paint("->"));
                    bits.push(Style::default().paint(" "));

                    if let Some(parent) = target.path.parent() {
                        self.add_parent_bits(&mut bits, parent);
                    }

                    if !target.name.is_empty() {
                        let target = FileName {
                            file: target,
                            colours: self.colours,
                            target: None,
                            link_style: LinkStyle::FullLinkPaths,
                            classify: Classify::JustFilenames,
                            exts: self.exts,
                        };

                        for bit in target.coloured_file_name() {
                            bits.push(bit);
                        }
                    }
                },

                FileTarget::Broken(ref broken_path) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_arrow.paint("->"));
                    bits.push(Style::default().paint(" "));
                    escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline());
                },

                FileTarget::Err(_) => {
                    // Do nothing -- the error gets displayed on the next line
                },
            }
        }
        else if let Classify::AddFileIndicators = self.classify {
            if let Some(class) = self.classify_char() {
                bits.push(Style::default().paint(class));
            }
        }

        bits.into()
    }

    /// Adds the bits of the parent path to the given bits vector.
    /// The path gets its characters escaped based on the colours.
    fn add_parent_bits(&self, bits: &mut Vec<ANSIString>, parent: &Path) {
        let coconut = parent.components().count();

        if coconut == 1 && parent.has_root() {
            bits.push(self.colours.symlink_path.paint("/"));
        }
        else if coconut >= 1 {
            escape(parent.to_string_lossy().to_string(), bits, self.colours.symlink_path, self.colours.control_char);
            bits.push(self.colours.symlink_path.paint("/"));
        }
    }

    /// The character to be displayed after a file when classifying is on, if
    /// the file’s type has one associated with it.
    fn classify_char(&self) -> Option<&'static str> {
        if self.file.is_executable_file() {
            Some("*")
        } else if self.file.is_directory() {
            Some("/")
        } else if self.file.is_pipe() {
            Some("|")
        } else if self.file.is_link() {
            Some("@")
        } else if self.file.is_socket() {
            Some("=")
        } else {
            None
        }
    }

    /// Returns at least one ANSI-highlighted string representing this file’s
    /// name using the given set of colours.
    ///
    /// Ordinarily, this will be just one string: the file’s complete name,
    /// coloured according to its file type. If the name contains control
    /// characters such as newlines or escapes, though, we can’t just print them
    /// to the screen directly, because then there’ll be newlines in weird places.
    ///
    /// So in that situation, those characters will be escaped and highlighted in
    /// a different colour.
    fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
        let file_style = self.style();
        let mut bits = Vec::new();
        escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char);
        bits
    }

    /// Figures out which colour to paint the filename part of the output,
    /// depending on which “type” of file it appears to be -- either from the
    /// class on the filesystem or from its name.
    pub fn style(&self) -> Style {

        // Override the style with the “broken link” style when this file is
        // a link that we can’t follow for whatever reason. This is used when
        // there’s no other place to show that the link doesn’t work.
        if let LinkStyle::JustFilenames = self.link_style {
            if let Some(ref target) = self.target {
                if target.is_broken() {
                    return self.colours.broken_arrow;
                }
            }
        }

        // Otherwise, just apply a bunch of rules in order. For example,
        // executable image files should be executable rather than images.
        match self.file {
            f if f.is_directory()        => self.colours.filekinds.directory,
            f if f.is_executable_file()  => self.colours.filekinds.executable,
            f if f.is_link()             => self.colours.filekinds.symlink,
            f if f.is_pipe()             => self.colours.filekinds.pipe,
            f if f.is_block_device()     => self.colours.filekinds.block_device,
            f if f.is_char_device()      => self.colours.filekinds.char_device,
            f if f.is_socket()           => self.colours.filekinds.socket,
            f if !f.is_file()            => self.colours.filekinds.special,

            f if self.exts.is_immediate(f)   => self.colours.filetypes.immediate,
            f if self.exts.is_image(f)       => self.colours.filetypes.image,
            f if self.exts.is_video(f)       => self.colours.filetypes.video,
            f if self.exts.is_music(f)       => self.colours.filetypes.music,
            f if self.exts.is_lossless(f)    => self.colours.filetypes.lossless,
            f if self.exts.is_crypto(f)      => self.colours.filetypes.crypto,
            f if self.exts.is_document(f)    => self.colours.filetypes.document,
            f if self.exts.is_compressed(f)  => self.colours.filetypes.compressed,
            f if self.exts.is_temp(f)        => self.colours.filetypes.temp,
            f if self.exts.is_compiled(f)    => self.colours.filetypes.compiled,

            _                                => self.colours.filekinds.normal,
        }
    }
}
<commit_msg>Use the file type colour trait methods<commit_after>use std::path::Path;

use ansi_term::{ANSIString, Style};

use fs::{File, FileTarget};
use info::filetype::FileExtensions;
use output::Colours;
use output::escape;
use output::cell::TextCellContents;

/// Basically a file name factory.
#[derive(Debug)]
pub struct FileStyle {

    /// Whether to append file class characters to file names.
    pub classify: Classify,

    /// Mapping of file extensions to colours, to highlight regular files.
    pub exts: FileExtensions,
}

impl FileStyle {

    /// Create a new `FileName` that prints the given file’s name, painting it
    /// with the remaining arguments.
    pub fn for_file<'a, 'dir>(&'a self, file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
        FileName {
            file, colours,
            link_style: LinkStyle::JustFilenames,
            exts:       &self.exts,
            classify:   self.classify,
            target:     if file.is_link() { Some(file.link_target()) }
                                     else { None }
        }
    }
}

/// When displaying a file name, there needs to be some way to handle broken
/// links, depending on how long the resulting Cell can be.
#[derive(PartialEq, Debug, Copy, Clone)]
enum LinkStyle {

    /// Just display the file names, but colour them differently if they’re
    /// a broken link or can’t be followed.
    JustFilenames,

    /// Display all files in their usual style, but follow each link with an
    /// arrow pointing to their path, colouring the path differently if it’s
    /// a broken link, and doing nothing if it can’t be followed.
    FullLinkPaths,
}

/// Whether to append file class characters to the file names.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum Classify {

    /// Just display the file names, without any characters.
    JustFilenames,

    /// Add a character after the file name depending on what class of file
    /// it is.
    AddFileIndicators,
}

impl Default for Classify {
    fn default() -> Classify {
        Classify::JustFilenames
    }
}

/// A **file name** holds all the information necessary to display the name
/// of the given file. This is used in all of the views.
pub struct FileName<'a, 'dir: 'a> {

    /// A reference to the file that we're getting the name of.
    file: &'a File<'dir>,

    /// The colours used to paint the file name and its surrounding text.
    colours: &'a Colours,

    /// The file that this file points to if it's a link.
    target: Option<FileTarget<'dir>>,

    /// How to handle displaying links.
    link_style: LinkStyle,

    /// Whether to append file class characters to file names.
    classify: Classify,

    /// Mapping of file extensions to colours, to highlight regular files.
    exts: &'a FileExtensions,
}

impl<'a, 'dir> FileName<'a, 'dir> {

    /// Sets the flag on this file name to display link targets with an
    /// arrow followed by their path.
    pub fn with_link_paths(mut self) -> Self {
        self.link_style = LinkStyle::FullLinkPaths;
        self
    }

    /// Paints the name of the file using the colours, resulting in a vector
    /// of coloured cells that can be printed to the terminal.
    ///
    /// This method returns some `TextCellContents`, rather than a `TextCell`,
    /// because for the last cell in a table, it doesn’t need to have its
    /// width calculated.
    pub fn paint(&self) -> TextCellContents {
        let mut bits = Vec::new();

        if self.file.parent_dir.is_none() {
            if let Some(parent) = self.file.path.parent() {
                self.add_parent_bits(&mut bits, parent);
            }
        }

        if !self.file.name.is_empty() {
            for bit in self.coloured_file_name() {
                bits.push(bit);
            }
        }

        if let (LinkStyle::FullLinkPaths, Some(target)) = (self.link_style, self.target.as_ref()) {
            match *target {
                FileTarget::Ok(ref target) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.punctuation.paint("->"));
                    bits.push(Style::default().paint(" "));

                    if let Some(parent) = target.path.parent() {
                        self.add_parent_bits(&mut bits, parent);
                    }

                    if !target.name.is_empty() {
                        let target = FileName {
                            file: target,
                            colours: self.colours,
                            target: None,
                            link_style: LinkStyle::FullLinkPaths,
                            classify: Classify::JustFilenames,
                            exts: self.exts,
                        };

                        for bit in target.coloured_file_name() {
                            bits.push(bit);
                        }
                    }
                },

                FileTarget::Broken(ref broken_path) => {
                    bits.push(Style::default().paint(" "));
                    bits.push(self.colours.broken_arrow.paint("->"));
                    bits.push(Style::default().paint(" "));
                    escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline());
                },

                FileTarget::Err(_) => {
                    // Do nothing -- the error gets displayed on the next line
                },
            }
        }
        else if let Classify::AddFileIndicators = self.classify {
            if let Some(class) = self.classify_char() {
                bits.push(Style::default().paint(class));
            }
        }

        bits.into()
    }

    /// Adds the bits of the parent path to the given bits vector.
    /// The path gets its characters escaped based on the colours.
    fn add_parent_bits(&self, bits: &mut Vec<ANSIString>, parent: &Path) {
        let coconut = parent.components().count();

        if coconut == 1 && parent.has_root() {
            bits.push(self.colours.symlink_path.paint("/"));
        }
        else if coconut >= 1 {
            escape(parent.to_string_lossy().to_string(), bits, self.colours.symlink_path, self.colours.control_char);
            bits.push(self.colours.symlink_path.paint("/"));
        }
    }

    /// The character to be displayed after a file when classifying is on, if
    /// the file’s type has one associated with it.
    fn classify_char(&self) -> Option<&'static str> {
        if self.file.is_executable_file() {
            Some("*")
        } else if self.file.is_directory() {
            Some("/")
        } else if self.file.is_pipe() {
            Some("|")
        } else if self.file.is_link() {
            Some("@")
        } else if self.file.is_socket() {
            Some("=")
        } else {
            None
        }
    }

    /// Returns at least one ANSI-highlighted string representing this file’s
    /// name using the given set of colours.
    ///
    /// Ordinarily, this will be just one string: the file’s complete name,
    /// coloured according to its file type. If the name contains control
    /// characters such as newlines or escapes, though, we can’t just print them
    /// to the screen directly, because then there’ll be newlines in weird places.
    ///
    /// So in that situation, those characters will be escaped and highlighted in
    /// a different colour.
    fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
        let file_style = self.style();
        let mut bits = Vec::new();
        escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char);
        bits
    }

    /// Figures out which colour to paint the filename part of the output,
    /// depending on which “type” of file it appears to be -- either from the
    /// class on the filesystem or from its name.
    pub fn style(&self) -> Style {
        use output::render::FiletypeColours;

        // Override the style with the “broken link” style when this file is
        // a link that we can’t follow for whatever reason. This is used when
        // there’s no other place to show that the link doesn’t work.
        if let LinkStyle::JustFilenames = self.link_style {
            if let Some(ref target) = self.target {
                if target.is_broken() {
                    return self.colours.broken_arrow;
                }
            }
        }

        // Otherwise, just apply a bunch of rules in order. For example,
        // executable image files should be executable rather than images.
        match self.file {
            f if f.is_directory()        => self.colours.directory(),
            f if f.is_executable_file()  => self.colours.filekinds.executable,
            f if f.is_link()             => self.colours.symlink(),
            f if f.is_pipe()             => self.colours.pipe(),
            f if f.is_block_device()     => self.colours.block_device(),
            f if f.is_char_device()      => self.colours.char_device(),
            f if f.is_socket()           => self.colours.socket(),
            f if !f.is_file()            => self.colours.special(),

            f if self.exts.is_immediate(f)   => self.colours.filetypes.immediate,
            f if self.exts.is_image(f)       => self.colours.filetypes.image,
            f if self.exts.is_video(f)       => self.colours.filetypes.video,
            f if self.exts.is_music(f)       => self.colours.filetypes.music,
            f if self.exts.is_lossless(f)    => self.colours.filetypes.lossless,
            f if self.exts.is_crypto(f)      => self.colours.filetypes.crypto,
            f if self.exts.is_document(f)    => self.colours.filetypes.document,
            f if self.exts.is_compressed(f)  => self.colours.filetypes.compressed,
            f if self.exts.is_temp(f)        => self.colours.filetypes.temp,
            f if self.exts.is_compiled(f)    => self.colours.filetypes.compiled,

            _                                => self.colours.normal(),
        }
    }
}
<|endoftext|>