<commit_before>//! Module providing the TermLogger Implementation

use log::{LogLevel, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError, set_logger, Log};
use time;
use term;
use term::{StderrTerminal, StdoutTerminal, Terminal, color};
use std::sync::{Mutex, MutexGuard};
use std::io::{Write, Error};
use super::SharedLogger;

/// The TermLogger struct. Provides a stderr/out based Logger implementation
///
/// Supports colored output
pub struct TermLogger {
    level: LogLevelFilter,
    stderr: Mutex<Box<StderrTerminal>>,
    stdout: Mutex<Box<StdoutTerminal>>,
}

impl TermLogger
{
    /// init function. Globally initializes the TermLogger as the one and only used log facility.
    ///
    /// Takes the desired LogLevel as argument. It cannot be changed later on.
    /// Fails if another Logger was already initialized.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # fn main() {
    /// let _ = TermLogger::init(LogLevelFilter::Info);
    /// # }
    /// ```
    #[allow(dead_code)]
    pub fn init(log_level: LogLevelFilter) -> Result<(), SetLoggerError> {
        set_logger(|max_log_level| {
            max_log_level.set(log_level.clone());
            TermLogger::new(log_level)
        })
    }

    /// allows to create a new logger, that can be independently used, no matter whats globally set.
    ///
    /// no macros are provided for this case and you probably
    /// dont want to use this function, but `init()``, if you dont want to build a `CombinedLogger`.
    ///
    /// Takes the desired LogLevel as argument. It cannot be changed later on.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # fn main() {
    /// let term_logger = TermLogger::new(LogLevelFilter::Info);
    /// # }
    /// ```
    #[allow(dead_code)]
    pub fn new(log_level: LogLevelFilter) -> Box<TermLogger> {
        Box::new(TermLogger { level: log_level, stderr: Mutex::new(term::stderr().unwrap()), stdout: Mutex::new(term::stdout().unwrap()) })
    }

    fn try_log_term<W>(&self, record: &LogRecord, mut term_lock: MutexGuard<Box<Terminal<Output=W> + Send>>) -> Result<(), Error>
        where W: Write + Sized
    {
        let cur_time = time::now();

        let color = match record.level() {
            LogLevel::Error => color::RED,
            LogLevel::Warn => color::YELLOW,
            LogLevel::Info => color::BLUE,
            LogLevel::Debug => color::CYAN,
            LogLevel::Trace => color::WHITE
        };

        try!(write!(term_lock, "{:02}:{:02}:{:02} [",
                    cur_time.tm_hour,
                    cur_time.tm_min,
                    cur_time.tm_sec));
        try!(term_lock.fg(color));
        try!(write!(term_lock, "{}", record.level()));
        try!(term_lock.reset());
        try!(write!(term_lock, "] "));

        match record.level() {
            LogLevel::Error |
            LogLevel::Warn  |
            LogLevel::Info  |
            LogLevel::Debug => {
                try!(writeln!(term_lock,
                    "{}: {}",
                        record.target(),
                        record.args()
                ));
            },
            LogLevel::Trace => {
                try!(writeln!(term_lock,
                    "{}: [{}:{}] - {}",
                        record.target(),
                        record.location().file(),
                        record.location().line(),
                        record.args()
                ));
            },
        };

        try!(term_lock.flush());

        Ok(())
    }

    fn try_log(&self, record: &LogRecord) -> Result<(), Error> {
        if self.enabled(record.metadata()) {
            if record.level() == LogLevel::Error {
                self.try_log_term(record, self.stderr.lock().unwrap())
            } else {
                self.try_log_term(record, self.stdout.lock().unwrap())
            }
        } else {
            Ok(())
        }
    }
}

impl Log for TermLogger
{
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let _ = self.try_log(record);
    }
}

impl SharedLogger for TermLogger
{
    fn level(&self) -> LogLevelFilter {
        self.level
    }

    fn as_log(self: Box<Self>) -> Box<Log> {
        Box::new(*self)
    }
}
<commit_msg>Removed unwrap() to avoid panic.<commit_after>//! Module providing the TermLogger Implementation

use log::{LogLevel, LogLevelFilter, LogMetadata, LogRecord, SetLoggerError, set_logger, Log};
use time;
use term;
use term::{StderrTerminal, StdoutTerminal, Terminal, color};
use std::error;
use std::sync::{Mutex, MutexGuard};
use std::io::{Write, Error};
use super::SharedLogger;

/// The TermLogger struct. Provides a stderr/out based Logger implementation
///
/// Supports colored output
pub struct TermLogger {
    level: LogLevelFilter,
    stderr: Mutex<Box<StderrTerminal>>,
    stdout: Mutex<Box<StdoutTerminal>>,
}

impl TermLogger
{
    /// init function. Globally initializes the TermLogger as the one and only used log facility.
    ///
    /// Takes the desired LogLevel as argument. It cannot be changed later on.
    /// Fails if another Logger was already initialized.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # fn main() {
    /// let _ = TermLogger::init(LogLevelFilter::Info);
    /// # }
    /// ```
    #[allow(dead_code)]
    pub fn init(log_level: LogLevelFilter) -> Result<(), Box<error::Error>> {
        let logger = try!(TermLogger::new(log_level).ok_or("a terminal couldn't be opened".to_string()));
        try!(set_logger(|max_log_level| {
            max_log_level.set(log_level.clone());
            logger
        }));
        Ok(())
    }

    /// allows to create a new logger, that can be independently used, no matter whats globally set.
    ///
    /// no macros are provided for this case and you probably
    /// dont want to use this function, but `init()``, if you dont want to build a `CombinedLogger`.
    ///
    /// Takes the desired LogLevel as argument. It cannot be changed later on.
    ///
    /// # Examples
    /// ```
    /// # extern crate simplelog;
    /// # use simplelog::*;
    /// # fn main() {
    /// let term_logger = TermLogger::new(LogLevelFilter::Info).unwrap();
    /// # }
    /// ```
    #[allow(dead_code)]
    pub fn new(log_level: LogLevelFilter) -> Option<Box<TermLogger>> {
        term::stderr().and_then(|stderr|
            term::stdout().map(|stdout| {
                Box::new(TermLogger { level: log_level, stderr: Mutex::new(stderr), stdout: Mutex::new(stdout) })
            })
        )
    }

    fn try_log_term<W>(&self, record: &LogRecord, mut term_lock: MutexGuard<Box<Terminal<Output=W> + Send>>) -> Result<(), Error>
        where W: Write + Sized
    {
        let cur_time = time::now();

        let color = match record.level() {
            LogLevel::Error => color::RED,
            LogLevel::Warn => color::YELLOW,
            LogLevel::Info => color::BLUE,
            LogLevel::Debug => color::CYAN,
            LogLevel::Trace => color::WHITE
        };

        try!(write!(term_lock, "{:02}:{:02}:{:02} [",
                    cur_time.tm_hour,
                    cur_time.tm_min,
                    cur_time.tm_sec));
        try!(term_lock.fg(color));
        try!(write!(term_lock, "{}", record.level()));
        try!(term_lock.reset());
        try!(write!(term_lock, "] "));

        match record.level() {
            LogLevel::Error |
            LogLevel::Warn  |
            LogLevel::Info  |
            LogLevel::Debug => {
                try!(writeln!(term_lock,
                    "{}: {}",
                        record.target(),
                        record.args()
                ));
            },
            LogLevel::Trace => {
                try!(writeln!(term_lock,
                    "{}: [{}:{}] - {}",
                        record.target(),
                        record.location().file(),
                        record.location().line(),
                        record.args()
                ));
            },
        };

        try!(term_lock.flush());

        Ok(())
    }

    fn try_log(&self, record: &LogRecord) -> Result<(), Error> {
        if self.enabled(record.metadata()) {
            if record.level() == LogLevel::Error {
                self.try_log_term(record, self.stderr.lock().unwrap())
            } else {
                self.try_log_term(record, self.stdout.lock().unwrap())
            }
        } else {
            Ok(())
        }
    }
}

impl Log for TermLogger
{
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let _ = self.try_log(record);
    }
}

impl SharedLogger for TermLogger
{
    fn level(&self) -> LogLevelFilter {
        self.level
    }

    fn as_log(self: Box<Self>) -> Box<Log> {
        Box::new(*self)
    }
}
<|endoftext|>


<commit_before>use crate::consts::constant_simple;
use crate::utils;
use crate::utils::{path_to_local_id, sugg};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{hir_id::HirId, intravisit::FnKind, Arm, Body, Expr, ExprKind, FnDecl, Pat, PatKind, StmtKind};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{source_map::Span, sym};

declare_clippy_lint! {
    /// **What it does:**
    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
    ///
    /// **Why is this bad?**
    /// Concise code helps focusing on behavior instead of boilerplate.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let foo: Option<i32> = None;
    /// match foo {
    ///     Some(v) => v,
    ///     None => 1,
    /// };
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.unwrap_or(1);
    /// ```
    pub MANUAL_UNWRAP_OR,
    complexity,
    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
}

declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);

impl LateLintPass<'_> for ManualUnwrapOr {
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        _: &'tcx FnDecl<'tcx>,
        body: &'tcx Body<'tcx>,
        span: Span,
        _: HirId,
    ) {
        if in_external_macro(cx.sess(), span) {
            return;
        }
        if_chain! {
            if let FnKind::ItemFn(_, _, header, _) = kind;
            if !header.is_const();
            let expr = &body.value;
            if let ExprKind::Block(block, _) = expr.kind;
            then {
                for stmt in block.stmts {
                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = &stmt.kind {
                        lint_manual_unwrap_or(cx, expr);
                    }
                }
                if let Some(expr) = block.expr {
                    lint_manual_unwrap_or(cx, expr);
                }
            }
        }
    }
}

#[derive(Copy, Clone)]
enum Case {
    Option,
    Result,
}

impl Case {
    fn unwrap_fn_path(&self) -> &str {
        match self {
            Case::Option => "Option::unwrap_or",
            Case::Result => "Result::unwrap_or",
        }
    }
}

fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
    fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
        if_chain! {
            if arms.len() == 2;
            if arms.iter().all(|arm| arm.guard.is_none());
            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
                match arm.pat.kind {
                    PatKind::Path(ref some_qpath) =>
                        utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
                    PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
                        utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
                    _ => false,
                }
            );
            let unwrap_arm = &arms[1 - idx];
            if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
            if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
                || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
            if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
            if path_to_local_id(unwrap_arm.body, binding_hir_id);
            if !utils::usage::contains_return_break_continue_macro(or_arm.body);
            then {
                Some(or_arm)
            } else {
                None
            }
        }
    }

    if_chain! {
        if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
        let ty = cx.typeck_results().expr_ty(scrutinee);
        if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) {
            Some(Case::Option)
        } else if is_type_diagnostic_item(cx, ty, sym::result_type) {
            Some(Case::Result)
        } else {
            None
        };
        if let Some(or_arm) = applicable_or_arm(match_arms);
        if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
        if let Some(indent) = indent_of(cx, expr.span);
        if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
        then {
            let reindented_or_body =
                reindent_multiline(or_body_snippet.into(), true, Some(indent));
            span_lint_and_sugg(
                cx,
                MANUAL_UNWRAP_OR, expr.span,
                &format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
                "replace with",
                format!(
                    "{}.unwrap_or({})",
                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
                    reindented_or_body,
                ),
                Applicability::MachineApplicable,
            );
        }
    }
}
<commit_msg>Use in_constant instead of is_const<commit_after>use crate::consts::constant_simple;
use crate::utils;
use crate::utils::{in_constant, path_to_local_id, sugg};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// **What it does:**
    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
    ///
    /// **Why is this bad?**
    /// Concise code helps focusing on behavior instead of boilerplate.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let foo: Option<i32> = None;
    /// match foo {
    ///     Some(v) => v,
    ///     None => 1,
    /// };
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.unwrap_or(1);
    /// ```
    pub MANUAL_UNWRAP_OR,
    complexity,
    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
}

declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);

impl LateLintPass<'_> for ManualUnwrapOr {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
        if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
            return;
        }
        lint_manual_unwrap_or(cx, expr);
    }
}

#[derive(Copy, Clone)]
enum Case {
    Option,
    Result,
}

impl Case {
    fn unwrap_fn_path(&self) -> &str {
        match self {
            Case::Option => "Option::unwrap_or",
            Case::Result => "Result::unwrap_or",
        }
    }
}

fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
    fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
        if_chain! {
            if arms.len() == 2;
            if arms.iter().all(|arm| arm.guard.is_none());
            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
                match arm.pat.kind {
                    PatKind::Path(ref some_qpath) =>
                        utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
                    PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
                        utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
                    _ => false,
                }
            );
            let unwrap_arm = &arms[1 - idx];
            if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
            if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
                || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
            if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
            if path_to_local_id(unwrap_arm.body, binding_hir_id);
            if !utils::usage::contains_return_break_continue_macro(or_arm.body);
            then {
                Some(or_arm)
            } else {
                None
            }
        }
    }

    if_chain! {
        if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
        let ty = cx.typeck_results().expr_ty(scrutinee);
        if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) {
            Some(Case::Option)
        } else if is_type_diagnostic_item(cx, ty, sym::result_type) {
            Some(Case::Result)
        } else {
            None
        };
        if let Some(or_arm) = applicable_or_arm(match_arms);
        if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
        if let Some(indent) = indent_of(cx, expr.span);
        if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
        then {
            let reindented_or_body =
                reindent_multiline(or_body_snippet.into(), true, Some(indent));
            span_lint_and_sugg(
                cx,
                MANUAL_UNWRAP_OR, expr.span,
                &format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
                "replace with",
                format!(
                    "{}.unwrap_or({})",
                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
                    reindented_or_body,
                ),
                Applicability::MachineApplicable,
            );
        }
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
extern crate groupable;

use std::collections::{HashMap, TreeMap, VecMap, TrieMap};
use groupable::Groupable;

static XS : &'static [char] = &['h', 'b', 'i', 'y', '!', 'e'];

macro_rules! test_string (
    ($t:ty) => (
        {
            let map = XS.iter().enumerate()
                           .map(|(i, &c)| (i % 2, c))
                           .group::<$t>();

            assert_eq!(map[0].as_slice(), "hi!");
            assert_eq!(map[1].as_slice(), "bye");
        }
    )
)

macro_rules! test_char_vec (
    ($t:ty) => (
        {
            let map = XS.iter().enumerate()
                           .map(|(i, &c)| (i % 2, c))
                           .group::<$t>();

            assert_eq!(map[0].as_slice(), ['h', 'i', '!'].as_slice());
            assert_eq!(map[1].as_slice(), ['b', 'y', 'e'].as_slice());
        }
    )
)

#[test]
fn hashmap() {
    test_string!(HashMap<uint, String>)
    test_char_vec!(HashMap<uint, Vec<char>>)
}

#[test]
fn treemap() {
    test_string!(TreeMap<uint, String>)
    test_char_vec!(TreeMap<uint, Vec<char>>)
}

#[test]
fn smallintmap() {
    test_string!(VecMap<String>)
    test_char_vec!(VecMap<Vec<char>>)
}

#[test]
fn triemap() {
    test_string!(TrieMap<String>)
    test_char_vec!(TrieMap<Vec<char>>)
}
<commit_msg>Add semicolons after macros in tests<commit_after>#![feature(macro_rules)]
extern crate groupable;

use std::collections::{HashMap, TreeMap, VecMap, TrieMap};
use groupable::Groupable;

static XS : &'static [char] = &['h', 'b', 'i', 'y', '!', 'e'];

macro_rules! test_string (
    ($t:ty) => (
        {
            let map = XS.iter().enumerate()
                           .map(|(i, &c)| (i % 2, c))
                           .group::<$t>();

            assert_eq!(map[0].as_slice(), "hi!");
            assert_eq!(map[1].as_slice(), "bye");
        }
    )
);

macro_rules! test_char_vec (
    ($t:ty) => (
        {
            let map = XS.iter().enumerate()
                           .map(|(i, &c)| (i % 2, c))
                           .group::<$t>();

            assert_eq!(map[0].as_slice(), ['h', 'i', '!'].as_slice());
            assert_eq!(map[1].as_slice(), ['b', 'y', 'e'].as_slice());
        }
    )
);

#[test]
fn hashmap() {
    test_string!(HashMap<uint, String>);
    test_char_vec!(HashMap<uint, Vec<char>>);
}

#[test]
fn treemap() {
    test_string!(TreeMap<uint, String>);
    test_char_vec!(TreeMap<uint, Vec<char>>);
}

#[test]
fn smallintmap() {
    test_string!(VecMap<String>);
    test_char_vec!(VecMap<Vec<char>>);
}

#[test]
fn triemap() {
    test_string!(TrieMap<String>);
    test_char_vec!(TrieMap<Vec<char>>);
}
<|endoftext|>


<commit_before>use clap::Parser;
use dicom_core::{dicom_value, smallvec};
use dicom_core::{DataElement, PrimitiveValue, VR};
use dicom_dictionary_std::tags;
use dicom_dump::DumpOptions;
use dicom_encoding::transfer_syntax;
use dicom_object::{mem::InMemDicomObject, open_file, StandardDataDictionary};
use dicom_transfer_syntax_registry::{entries, TransferSyntaxRegistry};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use smallvec::smallvec;
use snafu::{prelude::*, ErrorCompat};
use std::io::{stderr, Read};
use std::path::PathBuf;
use tracing::{debug, error, info, warn, Level};
use transfer_syntax::TransferSyntaxIndex;

/// DICOM C-FIND SCU
#[derive(Debug, Parser)]
struct App {
    /// socket address to FIND SCP (example: "127.0.0.1:1045")
    addr: String,
    /// the DICOM file representing the query object
    file: PathBuf,
    /// verbose mode
    #[clap(short = 'v', long = "verbose")]
    verbose: bool,
    /// the calling AE title
    #[clap(long = "calling-ae-title", default_value = "FIND-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[clap(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length
    #[clap(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,
}

fn report<E: 'static>(err: &E)
where
    E: std::error::Error,
{
    error!("{}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }
}

fn report_backtrace<E: 'static>(err: &E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

fn report_with_backtrace<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    report(&err);
    report_backtrace(&err);
}

fn main() {
    run().unwrap_or_else(|err| {
        report_with_backtrace(err);
        std::process::exit(-2);
    });
}

#[derive(Debug, Snafu)]
enum Error {
    /// Could not initialize SCU
    InitScu {
        source: dicom_ul::association::client::Error,
    },

    /// Could not construct DICOM command
    CreateCommand { source: dicom_object::Error },

    /// Could not read DICOM command
    ReadCommand { source: dicom_object::Error },

    /// Could not dump DICOM output
    DumpOutput { source: std::io::Error },

    #[snafu(whatever, display("{}", message))]
    Other {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error + 'static>, Some)))]
        source: Option<Box<dyn std::error::Error + 'static>>,
    },
}

fn run() -> Result<(), Error> {
    let App {
        addr,
        file,
        verbose,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
    } = App::from_args();

    tracing::subscriber::set_global_default(
        tracing_subscriber::FmtSubscriber::builder()
            .with_max_level(if verbose { Level::DEBUG } else { Level::INFO })
            .finish(),
    )
    .unwrap_or_else(|e| {
        report(&e);
    });

    if verbose {
        info!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file).context(CreateCommandSnafu)?;

    // Study Root Query/Retrieve Information Model – FIND
    let abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.1";

    if verbose {
        info!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(abstract_syntax)
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)
        .context(InitScuSnafu)?;

    if verbose {
        info!("Association established");
    }

    let pc_selected = if let Some(pc_selected) = scu.presentation_contexts().first() {
        pc_selected
    } else {
        error!("Could not choose a presentation context");
        let _ = scu.abort();
        std::process::exit(-2);
    };
    let pc_selected_id = pc_selected.id;

    let ts = if let Some(ts) = TransferSyntaxRegistry.get(&pc_selected.transfer_syntax) {
        ts
    } else {
        error!("Poorly negotiated transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    if verbose {
        debug!("Transfer Syntax: {}", ts.name());
    }

    let cmd = find_req_command("1.2.840.10008.5.1.4.1.2.2.1\0", 1);

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(&mut cmd_data, &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased())
        .whatever_context("Failed to write command")?;

    let implicit_vr_le = entries::IMPLICIT_VR_LITTLE_ENDIAN.erased();

    let mut iod_data = Vec::with_capacity(128);
    dicom_file
        .write_dataset_with_ts(&mut iod_data, &implicit_vr_le)
        .whatever_context("failed to write identifier dataset")?;

    let nbytes = cmd_data.len() + iod_data.len();

    if verbose {
        println!("Sending query (~ {} Kb)...", nbytes / 1024);
    }

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Command,
            is_last: true,
            data: cmd_data,
        }],
    };
    scu.send(&pdu).whatever_context("Could not send command")?;

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Data,
            is_last: true,
            data: iod_data,
        }],
    };
    scu.send(&pdu)
        .whatever_context("Could not send C-Find request")?;

    if verbose {
        debug!("Awaiting response...");
    }

    let mut i = 0;
    loop {
        let rsp_pdu = scu
            .receive()
            .whatever_context("Failed to receive response from remote node")?;

        match rsp_pdu {
            Pdu::PData { data } => {
                let data_value = &data[0];

                let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                    &data_value.data[..],
                    &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
                )
                .context(ReadCommandSnafu)?;
                if verbose {
                    eprintln!("Match #{} Response command:", i);
                    DumpOptions::new()
                        .dump_object_to(stderr(), &cmd_obj)
                        .context(DumpOutputSnafu)?;
                }
                let status = cmd_obj
                    .element(tags::STATUS)
                    .whatever_context("status code from response is missing")?
                    .to_int::<u16>()
                    .whatever_context("failed to read status code")?;
                if status == 0 {
                    if verbose {
                        debug!("Matching is complete");
                    }
                    break;
                } else if status == 0xFF00 || status == 0xFF01 {
                    if verbose {
                        debug!("Operation pending: {:x}", status);
                    }

                    // fetch DICOM data

                    let dcm = {
                        let mut rsp = scu.receive_pdata();
                        let mut response_data = Vec::new();
                        rsp.read_to_end(&mut response_data)
                            .whatever_context("Failed to read response data")?;

                        InMemDicomObject::read_dataset_with_ts(&response_data[..], &implicit_vr_le)
                            .whatever_context("Could not read response data set")?
                    };

                    println!("------------ Match #{} ------------", i);
                    DumpOptions::new()
                        .dump_object(&dcm)
                        .context(DumpOutputSnafu)?;
                    i += 1;
                } else {
                    warn!("Operation failed (status code {})", status);
                    break;
                }
            }

            pdu @ Pdu::Unknown { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::ReleaseRQ
            | pdu @ Pdu::ReleaseRP
            | pdu @ Pdu::AbortRQ { .. } => {
                error!("Unexpected SCP response: {:?}", pdu);
                let _ = scu.abort();
                std::process::exit(-2);
            }
        }
    }
    let _ = scu.release();

    Ok(())
}

fn find_req_command(
    sop_class_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::new_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            8 + even_len(sop_class_uid.len())   // SOP Class UID
            + 8 + 2 // command field
            + 8 + 2 // message ID
            + 8 + 2 // priority
            + 8 + 2, // data set type
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        PrimitiveValue::from(sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        // 0020H: C-FIND-RQ message
        dicom_value!(U16, [0x0020]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        // medium
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<commit_msg>[findscu] query matching WIP<commit_after>use clap::Parser;
use dicom_core::{dicom_value, smallvec};
use dicom_core::{DataElement, PrimitiveValue, VR};
use dicom_dictionary_std::tags;
use dicom_dump::DumpOptions;
use dicom_encoding::transfer_syntax;
use dicom_object::{mem::InMemDicomObject, open_file, StandardDataDictionary};
use dicom_transfer_syntax_registry::{entries, TransferSyntaxRegistry};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use smallvec::smallvec;
use snafu::{prelude::*, ErrorCompat};
use std::io::{stderr, Read};
use std::path::PathBuf;
use tracing::{debug, error, info, warn, Level};
use transfer_syntax::TransferSyntaxIndex;

#[derive(Debug, Clone, Eq, Hash, PartialEq)]
struct TermQuery {
    field: String,
    match_value: String,
}

/// DICOM C-FIND SCU
#[derive(Debug, Parser)]
struct App {
    /// socket address to FIND SCP (example: "127.0.0.1:1045")
    addr: String,
    /// the DICOM file representing the query object
    file: PathBuf,
    /// verbose mode
    #[clap(short = 'v', long = "verbose")]
    verbose: bool,
    /// the calling AE title
    #[clap(long = "calling-ae-title", default_value = "FIND-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[clap(long = "called-ae-title", default_value = "ANY-SCP")]
    called_ae_title: String,
    /// the maximum PDU length
    #[clap(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,

    /// use patient root information model
    #[clap(long, conflicts_with = "study")]
    patient: bool,
    /// use study root information model (default)
    #[clap(long, conflicts_with = "patient")]
    study: bool,

    /// queries
    #[clap(short('q'))]
    query: Vec<String>,
}

fn report<E: 'static>(err: &E)
where
    E: std::error::Error,
{
    error!("{}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }
}

fn report_backtrace<E: 'static>(err: &E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

fn report_with_backtrace<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    report(&err);
    report_backtrace(&err);
}

fn main() {
    run().unwrap_or_else(|err| {
        report_with_backtrace(err);
        std::process::exit(-2);
    });
}

#[derive(Debug, Snafu)]
enum Error {
    /// Could not initialize SCU
    InitScu {
        source: dicom_ul::association::client::Error,
    },

    /// Could not construct DICOM command
    CreateCommand { source: dicom_object::Error },

    /// Could not read DICOM command
    ReadCommand { source: dicom_object::Error },

    /// Could not dump DICOM output
    DumpOutput { source: std::io::Error },

    #[snafu(whatever, display("{}", message))]
    Other {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error + 'static>, Some)))]
        source: Option<Box<dyn std::error::Error + 'static>>,
    },
}

fn run() -> Result<(), Error> {
    let App {
        addr,
        file,
        verbose,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
        patient,
        study,
        query,
    } = App::from_args();

    tracing::subscriber::set_global_default(
        tracing_subscriber::FmtSubscriber::builder()
            .with_max_level(if verbose { Level::DEBUG } else { Level::INFO })
            .finish(),
    )
    .unwrap_or_else(|e| {
        report(&e);
    });

    if verbose {
        info!("Opening file '{}'...", file.display());
    }

    let dicom_file = open_file(file).context(CreateCommandSnafu)?;

    let abstract_syntax = match (patient, study) {
        // Patient Root Query/Retrieve Information Model - FIND
        (true, false) => "1.2.840.10008.5.1.4.1.2.1.1",
        // Study Root Query/Retrieve Information Model – FIND (default)
        (false, false) | (false, true) => "1.2.840.10008.5.1.4.1.2.2.1",
        // Series
        _ => unreachable!("Unexpected flag combination"),
    };

    if verbose {
        info!("Establishing association with '{}'...", &addr);
    }

    let mut scu = ClientAssociationOptions::new()
        .with_abstract_syntax(abstract_syntax)
        .calling_ae_title(calling_ae_title)
        .called_ae_title(called_ae_title)
        .max_pdu_length(max_pdu_length)
        .establish(addr)
        .context(InitScuSnafu)?;

    if verbose {
        info!("Association established");
    }

    let pc_selected = if let Some(pc_selected) = scu.presentation_contexts().first() {
        pc_selected
    } else {
        error!("Could not choose a presentation context");
        let _ = scu.abort();
        std::process::exit(-2);
    };
    let pc_selected_id = pc_selected.id;

    let ts = if let Some(ts) = TransferSyntaxRegistry.get(&pc_selected.transfer_syntax) {
        ts
    } else {
        error!("Poorly negotiated transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    if verbose {
        debug!("Transfer Syntax: {}", ts.name());
    }

    let cmd = find_req_command(abstract_syntax, 1);

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(&mut cmd_data, &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased())
        .whatever_context("Failed to write command")?;

    let implicit_vr_le = entries::IMPLICIT_VR_LITTLE_ENDIAN.erased();

    let mut iod_data = Vec::with_capacity(128);
    dicom_file
        .write_dataset_with_ts(&mut iod_data, &implicit_vr_le)
        .whatever_context("failed to write identifier dataset")?;

    let nbytes = cmd_data.len() + iod_data.len();

    if verbose {
        debug!("Sending query ({} B)...", nbytes);
    }

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Command,
            is_last: true,
            data: cmd_data,
        }],
    };
    scu.send(&pdu).whatever_context("Could not send command")?;

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Data,
            is_last: true,
            data: iod_data,
        }],
    };
    scu.send(&pdu)
        .whatever_context("Could not send C-Find request")?;

    if verbose {
        debug!("Awaiting response...");
    }

    let mut i = 0;
    loop {
        let rsp_pdu = scu
            .receive()
            .whatever_context("Failed to receive response from remote node")?;

        match rsp_pdu {
            Pdu::PData { data } => {
                let data_value = &data[0];

                let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                    &data_value.data[..],
                    &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
                )
                .context(ReadCommandSnafu)?;
                if verbose {
                    eprintln!("Match #{} Response command:", i);
                    DumpOptions::new()
                        .dump_object_to(stderr(), &cmd_obj)
                        .context(DumpOutputSnafu)?;
                }
                let status = cmd_obj
                    .element(tags::STATUS)
                    .whatever_context("status code from response is missing")?
                    .to_int::<u16>()
                    .whatever_context("failed to read status code")?;
                if status == 0 {
                    if verbose {
                        debug!("Matching is complete");
                    }
                    break;
                } else if status == 0xFF00 || status == 0xFF01 {
                    if verbose {
                        debug!("Operation pending: {:x}", status);
                    }

                    // fetch DICOM data

                    let dcm = {
                        let mut rsp = scu.receive_pdata();
                        let mut response_data = Vec::new();
                        rsp.read_to_end(&mut response_data)
                            .whatever_context("Failed to read response data")?;

                        InMemDicomObject::read_dataset_with_ts(&response_data[..], &implicit_vr_le)
                            .whatever_context("Could not read response data set")?
                    };

                    println!("------------ Match #{} ------------", i);
                    DumpOptions::new()
                        .dump_object(&dcm)
                        .context(DumpOutputSnafu)?;
                    i += 1;
                } else {
                    warn!("Operation failed (status code {})", status);
                    break;
                }
            }

            pdu @ Pdu::Unknown { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::ReleaseRQ
            | pdu @ Pdu::ReleaseRP
            | pdu @ Pdu::AbortRQ { .. } => {
                error!("Unexpected SCP response: {:?}", pdu);
                let _ = scu.abort();
                std::process::exit(-2);
            }
        }
    }
    let _ = scu.release();

    Ok(())
}

fn find_req_command(
    sop_class_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::new_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            8 + even_len(sop_class_uid.len())   // SOP Class UID
            + 8 + 2 // command field
            + 8 + 2 // message ID
            + 8 + 2 // priority
            + 8 + 2, // data set type
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        PrimitiveValue::from(sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        // 0020H: C-FIND-RQ message
        dicom_value!(U16, [0x0020]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        // medium
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagmail;
extern crate libimagerror;
extern crate libimagutil;
extern crate libimagentryref;

use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagmail::mail::Mail;
use libimagentryref::reference::Ref;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagutil::info_result::*;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-mail",
                                    &version!()[..],
                                    "Mail collection tool",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "import-mail" => import_mail(&rt),
                "list"        => list(&rt),
                "mail-store"  => mail_store(&rt),
                _             => debug!("Unknown command") // More error handling
            }
        });
}

fn import_mail(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("import-mail").unwrap();
    let path = scmd.value_of("path").unwrap(); // enforced by clap

    Mail::import_from_path(rt.store(), path)
        .map_err_trace()
        .map_info_str("Ok");
}

fn list(rt: &Runtime) {
    use libimagmail::error::MailErrorKind as MEK;
    use libimagmail::error::MapErrInto;

    let scmd = rt.cli().subcommand_matches("list").unwrap();
    let do_check_dead            = scmd.is_present("check-dead");
    let do_check_changed         = scmd.is_present("check-changed");
    let do_check_changed_content = scmd.is_present("check-changed-content");
    let do_check_changed_permiss = scmd.is_present("check-changed-permissions");
    let store = rt.store();

    let iter = match store.retrieve_for_module("ref") {
        Ok(iter) => iter.filter_map(|id| {
            Ref::get(store, id)
                .map_err_into(MEK::RefHandlingError)
                .and_then(|rf| Mail::from_ref(rf))
                .map_err_trace()
                .ok()
        }),
        Err(e)   => trace_error_exit(&e, 1),
    };

    fn list_mail(m: Mail) {
        let id = match m.get_message_id() {
            Ok(Some(f)) => f,
            Ok(None) => "<no id>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let from = match m.get_from() {
            Ok(Some(f)) => f,
            Ok(None) => "<no from>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let to = match m.get_to() {
            Ok(Some(f)) => f,
            Ok(None) => "<no to>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let subject = match m.get_subject() {
            Ok(Some(f)) => f,
            Ok(None) => "<no subject>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        println!("Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n",
                 from = from,
                 id   = id,
                 subj = subject,
                 to   = to
        );
    }

    // TODO: Implement lister type in libimagmail for this
    for mail in iter {
        list_mail(mail)
    }
}

fn mail_store(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("mail-store").unwrap();
    error!("This feature is currently not implemented.");
    unimplemented!()
}

<commit_msg>Remove unused variables<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagmail;
extern crate libimagerror;
extern crate libimagutil;
extern crate libimagentryref;

use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagmail::mail::Mail;
use libimagentryref::reference::Ref;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagutil::info_result::*;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-mail",
                                    &version!()[..],
                                    "Mail collection tool",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call {}", name);
            match name {
                "import-mail" => import_mail(&rt),
                "list"        => list(&rt),
                "mail-store"  => mail_store(&rt),
                _             => debug!("Unknown command") // More error handling
            }
        });
}

fn import_mail(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("import-mail").unwrap();
    let path = scmd.value_of("path").unwrap(); // enforced by clap

    let _ = Mail::import_from_path(rt.store(), path)
        .map_err_trace()
        .map_info_str("Ok");
}

fn list(rt: &Runtime) {
    use libimagmail::error::MailErrorKind as MEK;
    use libimagmail::error::MapErrInto;

    let store = rt.store();

    let iter = match store.retrieve_for_module("ref") {
        Ok(iter) => iter.filter_map(|id| {
            Ref::get(store, id)
                .map_err_into(MEK::RefHandlingError)
                .and_then(|rf| Mail::from_ref(rf))
                .map_err_trace()
                .ok()
        }),
        Err(e)   => trace_error_exit(&e, 1),
    };

    fn list_mail(m: Mail) {
        let id = match m.get_message_id() {
            Ok(Some(f)) => f,
            Ok(None) => "<no id>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let from = match m.get_from() {
            Ok(Some(f)) => f,
            Ok(None) => "<no from>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let to = match m.get_to() {
            Ok(Some(f)) => f,
            Ok(None) => "<no to>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        let subject = match m.get_subject() {
            Ok(Some(f)) => f,
            Ok(None) => "<no subject>".to_owned(),
            Err(e) => {
                trace_error(&e);
                "<error>".to_owned()
            },
        };

        println!("Mail: {id}\n\tFrom: {from}\n\tTo: {to}\n\t{subj}\n",
                 from = from,
                 id   = id,
                 subj = subject,
                 to   = to
        );
    }

    // TODO: Implement lister type in libimagmail for this
    for mail in iter {
        list_mail(mail)
    }
}

fn mail_store(rt: &Runtime) {
    let _ = rt.cli().subcommand_matches("mail-store").unwrap();
    error!("This feature is currently not implemented.");
    unimplemented!()
}

<|endoftext|>


<commit_before>use std::mem;

use runtime::Object;
use {class, Id, IdVector};
use super::{INSCopying, INSObject};

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
}

impl<'a, T> NSEnumerator<'a, T> {
	unsafe fn from_ptr(ptr: *Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr) }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject];
			if obj.is_null() {
				None
			} else {
				Some(mem::transmute(obj))
			}
		}
	}
}

pub trait INSArray<T: INSObject> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at<'a>(&'a self, index: uint) -> &'a T {
		unsafe {
			let result = msg_send![self objectAtIndex:index];
			mem::transmute(result)
		}
	}

	fn object_enumerator<'a>(&'a self) -> NSEnumerator<'a, T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *Self)
	}

	fn from_vec(vec: Vec<Id<T>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}
<commit_msg>Add ContravariantLifetime marker to NSEnumerator.<commit_after>use std::kinds::marker::ContravariantLifetime;
use std::mem;

use runtime::Object;
use {class, Id, IdVector};
use super::{INSCopying, INSObject};

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
	marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
	unsafe fn from_ptr(ptr: *Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject];
			if obj.is_null() {
				None
			} else {
				Some(mem::transmute(obj))
			}
		}
	}
}

pub trait INSArray<T: INSObject> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at<'a>(&'a self, index: uint) -> &'a T {
		unsafe {
			let result = msg_send![self objectAtIndex:index];
			mem::transmute(result)
		}
	}

	fn object_enumerator<'a>(&'a self) -> NSEnumerator<'a, T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *Self)
	}

	fn from_vec(vec: Vec<Id<T>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}
<|endoftext|>


<commit_before>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;

use std::fmt;
use std::error::Error;
use std::time::Duration;
use std::path::Path;
use iron::prelude::*;
use iron::{status, Handler};
use router::Router;
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 3;   // 3 days

// This is a generic error used in routes
#[derive(Debug)]
struct NoCrate;

impl fmt::Display for NoCrate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Crate not found.")
    }
}

impl Error for NoCrate {
    fn description(&self) -> &str { "No Crate" }
}

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/rustdoc/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version/", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version/*", rustdoc::rustdoc_html_server_handler);
        router.get("/crates/:name", crate_details::crate_details_handler);
        router.get("/crates/:name/", crate_details::crate_details_handler);
        router.get("/crates/:name/:version", crate_details::crate_details_handler);
        router.get("/crates/:name/:version/", crate_details::crate_details_handler);
        router.get("/crates/:name/:version/builds", builds::build_list_handler);
        router.get("/crates/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/source/:name/:version/", source::source_browser_handler);
        router.get("/source/:name/:version/*", source::source_browser_handler);
        router.get("/search", releases::search_handler);

        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut router_chain = Chain::new(router);
        router_chain.link_before(pool::Pool::new());
        router_chain.link_after(hbse);

        // FIXME: URGENT use correct path
        let static_handler = Static::new(Path::new("../cratesfyi-prefix/public_html"))
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then staticfile handler
        // return 404 if none of them return Ok
        match self.router_handler.handle(req) {
            Ok(res) => return Ok(res),
            Err(e) => debug!("{}", e.description())
        };

        // if router fails try to serve files from database first
        // and then try static handler. if all of them fails, return 404
        // TODO: Add a custom 404 page
        if let Ok(res) = self.database_file_handler.handle(req) {
            Ok(res)
        } else if let Ok(res) = self.static_handler.handle(req) {
            Ok(res)
        } else {
            Ok(Response::with((status::NotFound, "404 FAILED IN CRATESFYIHANDLER")))
        }
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).unwrap_or("*".to_string());

    let versions = {

        let mut versions = Vec::new();
        // get every version of a crate
        for row in &conn.query("SELECT version  \
                                FROM releases \
                                INNER JOIN crates ON crates.id = releases.crate_id \
                                WHERE crates.name = $1",
                                &[&name])
            .unwrap() {
                let version: String = row.get(0);
                versions.push(version);
            }

        // FIXME: Need to sort versions with semver, database is not keeping them sorted
        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = VersionReq::parse(&req_version).unwrap();

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            versions_sem.push(Version::parse(&version).unwrap());
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    // I got this from mdBook::src::utils
    use pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(&text, opts);
    html::push_html(&mut s, p);
    s
}

/// Starts cratesfyi web server
pub fn start_web_server() {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http("localhost:3000").unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server();
    }
}
<commit_msg>Serve static pages from CRATESFYI_PREFIX env var<commit_after>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;

use std::{env, fmt};
use std::error::Error;
use std::time::Duration;
use std::path::PathBuf;
use iron::prelude::*;
use iron::{status, Handler};
use router::Router;
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 3;   // 3 days

// This is a generic error used in routes
#[derive(Debug)]
struct NoCrate;

impl fmt::Display for NoCrate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Crate not found.")
    }
}

impl Error for NoCrate {
    fn description(&self) -> &str { "No Crate" }
}

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/rustdoc/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version/", rustdoc::rustdoc_redirector_handler);
        router.get("/rustdoc/:crate/:version/*", rustdoc::rustdoc_html_server_handler);
        router.get("/crates/:name", crate_details::crate_details_handler);
        router.get("/crates/:name/", crate_details::crate_details_handler);
        router.get("/crates/:name/:version", crate_details::crate_details_handler);
        router.get("/crates/:name/:version/", crate_details::crate_details_handler);
        router.get("/crates/:name/:version/builds", builds::build_list_handler);
        router.get("/crates/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/source/:name/:version/", source::source_browser_handler);
        router.get("/source/:name/:version/*", source::source_browser_handler);
        router.get("/search", releases::search_handler);

        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut router_chain = Chain::new(router);
        router_chain.link_before(pool::Pool::new());
        router_chain.link_after(hbse);

        let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX").unwrap()).join("public_html");
        let static_handler = Static::new(prefix)
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then staticfile handler
        // return 404 if none of them return Ok
        match self.router_handler.handle(req) {
            Ok(res) => return Ok(res),
            Err(e) => debug!("{}", e.description())
        };

        // if router fails try to serve files from database first
        // and then try static handler. if all of them fails, return 404
        // TODO: Add a custom 404 page
        if let Ok(res) = self.database_file_handler.handle(req) {
            Ok(res)
        } else if let Ok(res) = self.static_handler.handle(req) {
            Ok(res)
        } else {
            Ok(Response::with((status::NotFound, "404 FAILED IN CRATESFYIHANDLER")))
        }
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).unwrap_or("*".to_string());

    let versions = {

        let mut versions = Vec::new();
        // get every version of a crate
        for row in &conn.query("SELECT version  \
                                FROM releases \
                                INNER JOIN crates ON crates.id = releases.crate_id \
                                WHERE crates.name = $1",
                                &[&name])
            .unwrap() {
                let version: String = row.get(0);
                versions.push(version);
            }

        // FIXME: Need to sort versions with semver, database is not keeping them sorted
        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = VersionReq::parse(&req_version).unwrap();

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            versions_sem.push(Version::parse(&version).unwrap());
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    // I got this from mdBook::src::utils
    use pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(&text, opts);
    html::push_html(&mut s, p);
    s
}

/// Starts cratesfyi web server
pub fn start_web_server() {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http("localhost:3000").unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server();
    }
}
<|endoftext|>


<commit_before>extern crate winapi;
extern crate "kernel32-sys" as kernel32;

// All types used in winapi that are used in win/mod.rs.
pub use self::winapi::{
	INVALID_HANDLE_VALUE,
	HANDLE,
};

// All types used in winapi.
use self::winapi::{
	BOOL,
	LPCWSTR,
	ULONG_PTR,
	DWORD,
	/* start: CreateFile */
	LPSECURITY_ATTRIBUTES,
	OPEN_EXISTING,
	// access (read / write) mode.
	FILE_LIST_DIRECTORY,
	// share mode
	FILE_SHARE_READ,
	FILE_SHARE_WRITE,
	FILE_SHARE_DELETE,
	// file attributes
	FILE_FLAG_BACKUP_SEMANTICS,
	FILE_FLAG_OVERLAPPED,
	/* end: CreateFile */
};

// All functions used in winapi/kernel32.
use self::kernel32::{
	CreateIoCompletionPort,
	GetLastError,
	CreateFileW,
};

use std::ffi::AsOsStr;
use std::os::windows::OsStrExt;

use std::io;
use std::error::FromError;

use fsnotify::*;

/**
 * Takes the last error and produces an Error:Io (R) from it.
 */
pub unsafe fn last_error<T>() -> NotifyResult<T> {
	Err( FromError::from_error( io::Error::last_os_error() ) )
}

macro_rules! win_guard {
	( $call:expr ) => {
		{
			unsafe {
				let a = $call;
				if a == INVALID_HANDLE_VALUE { last_error() } else { Ok( a ) }
			}
		};
	}
}

/**
 * Converts a Path to a LPCWSTR (*const wchar_t) for use in FFI.
 */
pub fn path_to_utf16ptr( path: &Path ) -> LPCWSTR {
	// https://gist.github.com/aturon/9e2f4365c3a01684685c
	// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/ext.rs#L115-L117
	let mut buf: Vec<u16> = path.as_os_str().encode_wide().collect();
	buf.push( 0 ); // null terminator
	return buf.as_ptr();
}

pub fn create_io_completion_port( file_handle: HANDLE, existing_port_completeion: HANDLE ) -> NotifyResult<HANDLE> {
	return win_guard!( CreateIoCompletionPort( file_handle, existing_port_completeion, 0 as ULONG_PTR, 0 as DWORD ) );
}

pub fn file_handle( path: &Path ) -> NotifyResult<HANDLE> {
	let p = path_to_utf16ptr( path );
	return win_guard!( CreateFileW( p,
		FILE_LIST_DIRECTORY,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		0 as LPSECURITY_ATTRIBUTES,
		OPEN_EXISTING,
		FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED as DWORD,
		0 as HANDLE
	) );
}<commit_msg>windows: last_error is now safe.<commit_after>extern crate winapi;
extern crate "kernel32-sys" as kernel32;

// All types used in winapi that are used in win/mod.rs.
pub use self::winapi::{
	INVALID_HANDLE_VALUE,
	HANDLE,
};

// All types used in winapi.
use self::winapi::{
	BOOL,
	LPCWSTR,
	ULONG_PTR,
	DWORD,
	/* start: CreateFile */
	LPSECURITY_ATTRIBUTES,
	OPEN_EXISTING,
	// access (read / write) mode.
	FILE_LIST_DIRECTORY,
	// share mode
	FILE_SHARE_READ,
	FILE_SHARE_WRITE,
	FILE_SHARE_DELETE,
	// file attributes
	FILE_FLAG_BACKUP_SEMANTICS,
	FILE_FLAG_OVERLAPPED,
	/* end: CreateFile */
};

// All functions used in winapi/kernel32.
use self::kernel32::{
	CreateIoCompletionPort,
	GetLastError,
	CreateFileW,
};

use std::ffi::AsOsStr;
use std::os::windows::OsStrExt;

use std::io;
use std::error::FromError;

use fsnotify::*;

/**
 * Takes the last error and produces an Error:Io (R) from it.
 */
pub fn last_error<T>() -> NotifyResult<T> {
	Err( FromError::from_error( io::Error::last_os_error() ) )
}

macro_rules! win_guard {
	( $call:expr ) => {
		{
			let a = unsafe { $call };
			if a  == INVALID_HANDLE_VALUE { last_error() }
			else { Ok( a ) }
		};
	}
}

/**
 * Converts a Path to a LPCWSTR (*const wchar_t) for use in FFI.
 */
pub fn path_to_utf16ptr( path: &Path ) -> LPCWSTR {
	// https://gist.github.com/aturon/9e2f4365c3a01684685c
	// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/ext.rs#L115-L117
	let mut buf: Vec<u16> = path.as_os_str().encode_wide().collect();
	buf.push( 0 ); // null terminator
	return buf.as_ptr();
}

pub fn create_io_completion_port( file_handle: HANDLE, existing_port_completeion: HANDLE ) -> NotifyResult<HANDLE> {
	return win_guard!( CreateIoCompletionPort( file_handle, existing_port_completeion, 0 as ULONG_PTR, 0 as DWORD ) );
}

pub fn file_handle( path: &Path ) -> NotifyResult<HANDLE> {
	let p = path_to_utf16ptr( path );
	return win_guard!( CreateFileW( p,
		FILE_LIST_DIRECTORY,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		0 as LPSECURITY_ATTRIBUTES,
		OPEN_EXISTING,
		FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED as DWORD,
		0 as HANDLE
	) );
}<|endoftext|>