<commit_before>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::{ThinTokenStream, TokenStream};

/// **What it does:** This lint warns when you use `println!("")` to
/// print a newline.
///
/// **Why is this bad?** You should use `println!()`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// println!("");
/// ```
declare_clippy_lint! {
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```rust
/// println!("Hello {}!", name);
/// ```
declare_clippy_lint! {
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

/// **What it does:** Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// **Why is this bad?** People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// **Known problems:** Only catches `print!` and `println!` calls.
///
/// **Example:**
/// ```rust
/// println!("Hello world!");
/// ```
declare_clippy_lint! {
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in in user-facing output.
///
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
declare_clippy_lint! {
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

/// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
///
/// **Why is this bad?** Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `println!("{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```rust
/// println!("foo");
/// ```
declare_clippy_lint! {
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

/// **What it does:** This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// writeln!("");
/// ```
declare_clippy_lint! {
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `writeln!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// write!(buf, "Hello {}!\n", name);
/// ```
declare_clippy_lint! {
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

/// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// **Why is this bad?** Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// writeln!(buf, "{}", "foo");
/// ```
declare_clippy_lint! {
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            PRINT_WITH_NEWLINE,
            PRINTLN_EMPTY_STRING,
            PRINT_STDOUT,
            USE_DEBUG,
            PRINT_LITERAL,
            WRITE_WITH_NEWLINE,
            WRITELN_EMPTY_STRING,
            WRITE_LITERAL
        )
    }
}

impl EarlyLintPass for Pass {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == "println" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if fmtstr == "" {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == "print" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a \
                         single newline, consider using `println!()` instead",
                    );
                }
            }
        } else if mac.node.path == "write" {
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a \
                         single newline, consider using `writeln!()` instead",
                    );
                }
            }
        } else if mac.node.path == "writeln" {
            let check_tts = check_tts(cx, &mac.node.tts, true);
            if let Some(fmtstr) = check_tts.0 {
                if fmtstr == "" {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = check_tts.1.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// options. The first part of the tuple is `format_str` of the macros. The second part of the tuple
/// is in the `write[ln]!` case the expression the `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust,ignore
/// writeln!(buf, "string to write: {}", something)
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -> (Option<String>, Option<Expr>) {
    use fmt_macros::*;
    let tts = TokenStream::from(tts.clone());
    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let fmtstr = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok(token) => token.0.to_string(),
        Err(_) => return (None, expr),
    };
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            width: CountImplied,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr);
        }
        let token_expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(expr) => expr,
            Err(_) => return (Some(fmtstr), None),
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

// Checks if `s` constains a single newline that terminates it
fn check_newlines(s: &str) -> bool {
    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<commit_msg>Fix breakage due to rust-lang/rust#57755<commit_after>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::{parser, token};
use syntax::tokenstream::TokenStream;

/// **What it does:** This lint warns when you use `println!("")` to
/// print a newline.
///
/// **Why is this bad?** You should use `println!()`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// println!("");
/// ```
declare_clippy_lint! {
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```rust
/// println!("Hello {}!", name);
/// ```
declare_clippy_lint! {
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

/// **What it does:** Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// **Why is this bad?** People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// **Known problems:** Only catches `print!` and `println!` calls.
///
/// **Example:**
/// ```rust
/// println!("Hello world!");
/// ```
declare_clippy_lint! {
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in in user-facing output.
///
/// **Example:**
/// ```rust
/// println!("{:?}", foo);
/// ```
declare_clippy_lint! {
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

/// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
///
/// **Why is this bad?** Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `println!("{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```rust
/// println!("foo");
/// ```
declare_clippy_lint! {
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

/// **What it does:** This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// writeln!("");
/// ```
declare_clippy_lint! {
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(\"\")` with an empty string"
}

/// **What it does:** This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `writeln!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// write!(buf, "Hello {}!\n", name);
/// ```
declare_clippy_lint! {
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

/// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// **Why is this bad?** Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// **Known problems:** Will also warn with macro calls as arguments that expand to literals
/// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
///
/// **Example:**
/// ```rust
/// writeln!(buf, "{}", "foo");
/// ```
declare_clippy_lint! {
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            PRINT_WITH_NEWLINE,
            PRINTLN_EMPTY_STRING,
            PRINT_STDOUT,
            USE_DEBUG,
            PRINT_LITERAL,
            WRITE_WITH_NEWLINE,
            WRITELN_EMPTY_STRING,
            WRITE_LITERAL
        )
    }
}

impl EarlyLintPass for Pass {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.node.path == "println" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if fmtstr == "" {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.node.path == "print" {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a \
                         single newline, consider using `println!()` instead",
                    );
                }
            }
        } else if mac.node.path == "write" {
            if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 {
                if check_newlines(&fmtstr) {
                    span_lint(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a \
                         single newline, consider using `writeln!()` instead",
                    );
                }
            }
        } else if mac.node.path == "writeln" {
            let check_tts = check_tts(cx, &mac.node.tts, true);
            if let Some(fmtstr) = check_tts.0 {
                if fmtstr == "" {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = check_tts.1.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// options. The first part of the tuple is `format_str` of the macros. The second part of the tuple
/// is in the `write[ln]!` case the expression the `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust,ignore
/// writeln!(buf, "string to write: {}", something)
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<String>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();
    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let fmtstr = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok(token) => token.0.to_string(),
        Err(_) => return (None, expr),
    };
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            width: CountImplied,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr);
        }
        let token_expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(expr) => expr,
            Err(_) => return (Some(fmtstr), None),
        };
        match &token_expr.node {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.node {
                    if let ExprKind::Path(_, p) = &lhs.node {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

// Checks if `s` constains a single newline that terminates it
fn check_newlines(s: &str) -> bool {
    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<|endoftext|>


<commit_before>use fmt_macros::{Parser, Piece, Position};

use crate::hir::def_id::DefId;
use crate::ty::{self, TyCtxt, GenericParamDefKind};
use crate::util::common::ErrorReported;
use crate::util::nodemap::FxHashMap;

use syntax::ast::{MetaItem, NestedMetaItem};
use syntax::attr;
use syntax::symbol::{Symbol, kw, sym};
use syntax_pos::Span;
use syntax_pos::symbol::LocalInternedString;

#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(LocalInternedString);

#[derive(Debug)]
pub struct OnUnimplementedDirective {
    pub condition: Option<MetaItem>,
    pub subcommands: Vec<OnUnimplementedDirective>,
    pub message: Option<OnUnimplementedFormatString>,
    pub label: Option<OnUnimplementedFormatString>,
    pub note: Option<OnUnimplementedFormatString>,
}

pub struct OnUnimplementedNote {
    pub message: Option<String>,
    pub label: Option<String>,
    pub note: Option<String>,
}

impl OnUnimplementedNote {
    pub fn empty() -> Self {
        OnUnimplementedNote { message: None, label: None, note: None }
    }
}

fn parse_error(
    tcx: TyCtxt<'_>,
    span: Span,
    message: &str,
    label: &str,
    note: Option<&str>,
) -> ErrorReported {
    let mut diag = struct_span_err!(
        tcx.sess, span, E0232, "{}", message);
    diag.span_label(span, label);
    if let Some(note) = note {
        diag.note(note);
    }
    diag.emit();
    ErrorReported
}

impl<'tcx> OnUnimplementedDirective {
    fn parse(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        items: &[NestedMetaItem],
        span: Span,
        is_root: bool,
    ) -> Result<Self, ErrorReported> {
        let mut errored = false;
        let mut item_iter = items.iter();

        let condition = if is_root {
            None
        } else {
            let cond = item_iter.next().ok_or_else(||
                parse_error(tcx, span,
                            "empty `on`-clause in `#[rustc_on_unimplemented]`",
                            "empty on-clause here",
                            None)
            )?.meta_item().ok_or_else(||
                parse_error(tcx, span,
                            "invalid `on`-clause in `#[rustc_on_unimplemented]`",
                            "invalid on-clause here",
                            None)
            )?;
            attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
            Some(cond.clone())
        };

        let mut message = None;
        let mut label = None;
        let mut note = None;
        let mut subcommands = vec![];
        for item in item_iter {
            if item.check_name(sym::message) && message.is_none() {
                if let Some(message_) = item.value_str() {
                    message = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, message_.as_str(), span)?);
                    continue;
                }
            } else if item.check_name(sym::label) && label.is_none() {
                if let Some(label_) = item.value_str() {
                    label = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, label_.as_str(), span)?);
                    continue;
                }
            } else if item.check_name(sym::note) && note.is_none() {
                if let Some(note_) = item.value_str() {
                    note = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, note_.as_str(), span)?);
                    continue;
                }
            } else if item.check_name(sym::on) && is_root &&
                message.is_none() && label.is_none() && note.is_none()
            {
                if let Some(items) = item.meta_item_list() {
                    if let Ok(subcommand) =
                        Self::parse(tcx, trait_def_id, &items, item.span(), false)
                    {
                        subcommands.push(subcommand);
                    } else {
                        errored = true;
                    }
                    continue
                }
            }

            // nothing found
            parse_error(tcx, item.span(),
                        "this attribute must have a valid value",
                        "expected value here",
                        Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#));
        }

        if errored {
            Err(ErrorReported)
        } else {
            Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
        }
    }

    pub fn of_item(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        impl_def_id: DefId,
    ) -> Result<Option<Self>, ErrorReported> {
        let attrs = tcx.get_attrs(impl_def_id);

        let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
            item
        } else {
            return Ok(None);
        };

        let result = if let Some(items) = attr.meta_item_list() {
            Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
        } else if let Some(value) = attr.value_str() {
            Ok(Some(OnUnimplementedDirective {
                condition: None,
                message: None,
                subcommands: vec![],
                label: Some(OnUnimplementedFormatString::try_parse(
                    tcx, trait_def_id, value.as_str(), attr.span)?),
                note: None,
            }))
        } else {
            return Err(ErrorReported);
        };
        debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
        result
    }

    pub fn evaluate(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_ref: ty::TraitRef<'tcx>,
        options: &[(Symbol, Option<String>)],
    ) -> OnUnimplementedNote {
        let mut message = None;
        let mut label = None;
        let mut note = None;
        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);

        for command in self.subcommands.iter().chain(Some(self)).rev() {
            if let Some(ref condition) = command.condition {
                if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
                    c.ident().map_or(false, |ident| {
                        options.contains(&(
                            ident.name,
                            c.value_str().map(|s| s.as_str().to_string())
                        ))
                    })
                }) {
                    debug!("evaluate: skipping {:?} due to condition", command);
                    continue
                }
            }
            debug!("evaluate: {:?} succeeded", command);
            if let Some(ref message_) = command.message {
                message = Some(message_.clone());
            }

            if let Some(ref label_) = command.label {
                label = Some(label_.clone());
            }

            if let Some(ref note_) = command.note {
                note = Some(note_.clone());
            }
        }

        let options: FxHashMap<Symbol, String> = options.into_iter()
            .filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned())))
            .collect();
        OnUnimplementedNote {
            label: label.map(|l| l.format(tcx, trait_ref, &options)),
            message: message.map(|m| m.format(tcx, trait_ref, &options)),
            note: note.map(|n| n.format(tcx, trait_ref, &options)),
        }
    }
}

impl<'tcx> OnUnimplementedFormatString {
    fn try_parse(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        from: LocalInternedString,
        err_sp: Span,
    ) -> Result<Self, ErrorReported> {
        let result = OnUnimplementedFormatString(from);
        result.verify(tcx, trait_def_id, err_sp)?;
        Ok(result)
    }

    fn verify(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        span: Span,
    ) -> Result<(), ErrorReported> {
        let name = tcx.item_name(trait_def_id);
        let generics = tcx.generics_of(trait_def_id);
        let parser = Parser::new(&self.0, None, vec![], false);
        let mut result = Ok(());
        for token in parser {
            match token {
                Piece::String(_) => (), // Normal string, no need to check it
                Piece::NextArgument(a) => match a.position {
                    // `{Self}` is allowed
                    Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
                    // `{ThisTraitsName}` is allowed
                    Position::ArgumentNamed(s) if s == name => (),
                    // `{from_method}` is allowed
                    Position::ArgumentNamed(s) if s == sym::from_method => (),
                    // `{from_desugaring}` is allowed
                    Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
                    // So is `{A}` if A is a type parameter
                    Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
                        param.name.as_symbol() == s
                    }) {
                        Some(_) => (),
                        None => {
                            span_err!(tcx.sess, span, E0230,
                                      "there is no parameter `{}` on trait `{}`", s, name);
                            result = Err(ErrorReported);
                        }
                    },
                    // `{:1}` and `{}` are not to be used
                    Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
                        span_err!(tcx.sess, span, E0231,
                                  "only named substitution parameters are allowed");
                        result = Err(ErrorReported);
                    }
                }
            }
        }

        result
    }

    pub fn format(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_ref: ty::TraitRef<'tcx>,
        options: &FxHashMap<Symbol, String>,
    ) -> String {
        let name = tcx.item_name(trait_ref.def_id);
        let trait_str = tcx.def_path_str(trait_ref.def_id);
        let generics = tcx.generics_of(trait_ref.def_id);
        let generic_map = generics.params.iter().filter_map(|param| {
            let value = match param.kind {
                GenericParamDefKind::Type { .. } |
                GenericParamDefKind::Const => {
                    trait_ref.substs[param.index as usize].to_string()
                },
                GenericParamDefKind::Lifetime => return None
            };
            let name = param.name.as_symbol();
            Some((name, value))
        }).collect::<FxHashMap<Symbol, String>>();
        let empty_string = String::new();

        let parser = Parser::new(&self.0, None, vec![], false);
        parser.map(|p|
            match p {
                Piece::String(s) => s,
                Piece::NextArgument(a) => match a.position {
                    Position::ArgumentNamed(s) => match generic_map.get(&s) {
                        Some(val) => val,
                        None if s == name => {
                            &trait_str
                        }
                        None => {
                            if let Some(val) = options.get(&s) {
                                val
                            } else if s == sym::from_desugaring || s == sym::from_method {
                                // don't break messages using these two arguments incorrectly
                                &empty_string
                            } else {
                                bug!("broken on_unimplemented {:?} for {:?}: \
                                      no argument matching {:?}",
                                     self.0, trait_ref, s)
                            }
                        }
                    },
                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0)
                }
            }
        ).collect()
    }
}
<commit_msg>Remove `LocalInternedString` uses from `librustc/traits/`.<commit_after>use fmt_macros::{Parser, Piece, Position};

use crate::hir::def_id::DefId;
use crate::ty::{self, TyCtxt, GenericParamDefKind};
use crate::util::common::ErrorReported;
use crate::util::nodemap::FxHashMap;

use syntax::ast::{MetaItem, NestedMetaItem};
use syntax::attr;
use syntax::symbol::{Symbol, kw, sym};
use syntax_pos::Span;

#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(Symbol);

#[derive(Debug)]
pub struct OnUnimplementedDirective {
    pub condition: Option<MetaItem>,
    pub subcommands: Vec<OnUnimplementedDirective>,
    pub message: Option<OnUnimplementedFormatString>,
    pub label: Option<OnUnimplementedFormatString>,
    pub note: Option<OnUnimplementedFormatString>,
}

pub struct OnUnimplementedNote {
    pub message: Option<String>,
    pub label: Option<String>,
    pub note: Option<String>,
}

impl OnUnimplementedNote {
    pub fn empty() -> Self {
        OnUnimplementedNote { message: None, label: None, note: None }
    }
}

fn parse_error(
    tcx: TyCtxt<'_>,
    span: Span,
    message: &str,
    label: &str,
    note: Option<&str>,
) -> ErrorReported {
    let mut diag = struct_span_err!(
        tcx.sess, span, E0232, "{}", message);
    diag.span_label(span, label);
    if let Some(note) = note {
        diag.note(note);
    }
    diag.emit();
    ErrorReported
}

impl<'tcx> OnUnimplementedDirective {
    fn parse(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        items: &[NestedMetaItem],
        span: Span,
        is_root: bool,
    ) -> Result<Self, ErrorReported> {
        let mut errored = false;
        let mut item_iter = items.iter();

        let condition = if is_root {
            None
        } else {
            let cond = item_iter.next().ok_or_else(||
                parse_error(tcx, span,
                            "empty `on`-clause in `#[rustc_on_unimplemented]`",
                            "empty on-clause here",
                            None)
            )?.meta_item().ok_or_else(||
                parse_error(tcx, span,
                            "invalid `on`-clause in `#[rustc_on_unimplemented]`",
                            "invalid on-clause here",
                            None)
            )?;
            attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
            Some(cond.clone())
        };

        let mut message = None;
        let mut label = None;
        let mut note = None;
        let mut subcommands = vec![];
        for item in item_iter {
            if item.check_name(sym::message) && message.is_none() {
                if let Some(message_) = item.value_str() {
                    message = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, message_, span)?);
                    continue;
                }
            } else if item.check_name(sym::label) && label.is_none() {
                if let Some(label_) = item.value_str() {
                    label = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, label_, span)?);
                    continue;
                }
            } else if item.check_name(sym::note) && note.is_none() {
                if let Some(note_) = item.value_str() {
                    note = Some(OnUnimplementedFormatString::try_parse(
                        tcx, trait_def_id, note_, span)?);
                    continue;
                }
            } else if item.check_name(sym::on) && is_root &&
                message.is_none() && label.is_none() && note.is_none()
            {
                if let Some(items) = item.meta_item_list() {
                    if let Ok(subcommand) =
                        Self::parse(tcx, trait_def_id, &items, item.span(), false)
                    {
                        subcommands.push(subcommand);
                    } else {
                        errored = true;
                    }
                    continue
                }
            }

            // nothing found
            parse_error(tcx, item.span(),
                        "this attribute must have a valid value",
                        "expected value here",
                        Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#));
        }

        if errored {
            Err(ErrorReported)
        } else {
            Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
        }
    }

    pub fn of_item(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        impl_def_id: DefId,
    ) -> Result<Option<Self>, ErrorReported> {
        let attrs = tcx.get_attrs(impl_def_id);

        let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
            item
        } else {
            return Ok(None);
        };

        let result = if let Some(items) = attr.meta_item_list() {
            Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
        } else if let Some(value) = attr.value_str() {
            Ok(Some(OnUnimplementedDirective {
                condition: None,
                message: None,
                subcommands: vec![],
                label: Some(OnUnimplementedFormatString::try_parse(
                    tcx, trait_def_id, value, attr.span)?),
                note: None,
            }))
        } else {
            return Err(ErrorReported);
        };
        debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
        result
    }

    pub fn evaluate(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_ref: ty::TraitRef<'tcx>,
        options: &[(Symbol, Option<String>)],
    ) -> OnUnimplementedNote {
        let mut message = None;
        let mut label = None;
        let mut note = None;
        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);

        for command in self.subcommands.iter().chain(Some(self)).rev() {
            if let Some(ref condition) = command.condition {
                if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
                    c.ident().map_or(false, |ident| {
                        options.contains(&(
                            ident.name,
                            c.value_str().map(|s| s.as_str().to_string())
                        ))
                    })
                }) {
                    debug!("evaluate: skipping {:?} due to condition", command);
                    continue
                }
            }
            debug!("evaluate: {:?} succeeded", command);
            if let Some(ref message_) = command.message {
                message = Some(message_.clone());
            }

            if let Some(ref label_) = command.label {
                label = Some(label_.clone());
            }

            if let Some(ref note_) = command.note {
                note = Some(note_.clone());
            }
        }

        let options: FxHashMap<Symbol, String> = options.into_iter()
            .filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned())))
            .collect();
        OnUnimplementedNote {
            label: label.map(|l| l.format(tcx, trait_ref, &options)),
            message: message.map(|m| m.format(tcx, trait_ref, &options)),
            note: note.map(|n| n.format(tcx, trait_ref, &options)),
        }
    }
}

impl<'tcx> OnUnimplementedFormatString {
    fn try_parse(
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        from: Symbol,
        err_sp: Span,
    ) -> Result<Self, ErrorReported> {
        let result = OnUnimplementedFormatString(from);
        result.verify(tcx, trait_def_id, err_sp)?;
        Ok(result)
    }

    fn verify(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_def_id: DefId,
        span: Span,
    ) -> Result<(), ErrorReported> {
        let name = tcx.item_name(trait_def_id);
        let generics = tcx.generics_of(trait_def_id);
        let s = self.0.as_str();
        let parser = Parser::new(&s, None, vec![], false);
        let mut result = Ok(());
        for token in parser {
            match token {
                Piece::String(_) => (), // Normal string, no need to check it
                Piece::NextArgument(a) => match a.position {
                    // `{Self}` is allowed
                    Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
                    // `{ThisTraitsName}` is allowed
                    Position::ArgumentNamed(s) if s == name => (),
                    // `{from_method}` is allowed
                    Position::ArgumentNamed(s) if s == sym::from_method => (),
                    // `{from_desugaring}` is allowed
                    Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
                    // So is `{A}` if A is a type parameter
                    Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
                        param.name.as_symbol() == s
                    }) {
                        Some(_) => (),
                        None => {
                            span_err!(tcx.sess, span, E0230,
                                      "there is no parameter `{}` on trait `{}`", s, name);
                            result = Err(ErrorReported);
                        }
                    },
                    // `{:1}` and `{}` are not to be used
                    Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
                        span_err!(tcx.sess, span, E0231,
                                  "only named substitution parameters are allowed");
                        result = Err(ErrorReported);
                    }
                }
            }
        }

        result
    }

    pub fn format(
        &self,
        tcx: TyCtxt<'tcx>,
        trait_ref: ty::TraitRef<'tcx>,
        options: &FxHashMap<Symbol, String>,
    ) -> String {
        let name = tcx.item_name(trait_ref.def_id);
        let trait_str = tcx.def_path_str(trait_ref.def_id);
        let generics = tcx.generics_of(trait_ref.def_id);
        let generic_map = generics.params.iter().filter_map(|param| {
            let value = match param.kind {
                GenericParamDefKind::Type { .. } |
                GenericParamDefKind::Const => {
                    trait_ref.substs[param.index as usize].to_string()
                },
                GenericParamDefKind::Lifetime => return None
            };
            let name = param.name.as_symbol();
            Some((name, value))
        }).collect::<FxHashMap<Symbol, String>>();
        let empty_string = String::new();

        let s = self.0.as_str();
        let parser = Parser::new(&s, None, vec![], false);
        parser.map(|p|
            match p {
                Piece::String(s) => s,
                Piece::NextArgument(a) => match a.position {
                    Position::ArgumentNamed(s) => match generic_map.get(&s) {
                        Some(val) => val,
                        None if s == name => {
                            &trait_str
                        }
                        None => {
                            if let Some(val) = options.get(&s) {
                                val
                            } else if s == sym::from_desugaring || s == sym::from_method {
                                // don't break messages using these two arguments incorrectly
                                &empty_string
                            } else {
                                bug!("broken on_unimplemented {:?} for {:?}: \
                                      no argument matching {:?}",
                                     self.0, trait_ref, s)
                            }
                        }
                    },
                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0)
                }
            }
        ).collect()
    }
}
<|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
//

//! Event types
//!
//! This module contains types that represent events. These types (which wrap FileLockEntries from
//! the Store) represent Events, thus they have functionality for settings the start and end-time,
//! getting the start and end time and also deleting start and end time.
//!

use chrono::naive::datetime::NaiveDateTime;

use libimagstore::store::Entry;
use libimagerror::into::IntoError;

use error::TimeTrackErrorKind as TTEK;
use error::MapErrInto;
use result::Result;
use constants::*;

use toml::Value;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadExt;

pub trait TimeTracking {

    fn set_start_datetime(&mut self, dt: NaiveDateTime) -> Result<()>;

    fn get_start_datetime(&self) -> Result<Option<NaiveDateTime>>;

    fn delete_start_datetime(&mut self) -> Result<()>;

    fn set_end_datetime(&mut self, dt: NaiveDateTime) -> Result<()>;

    fn get_end_datetime(&self) -> Result<Option<NaiveDateTime>>;

    fn delete_end_datetime(&mut self) -> Result<()>;

    fn valid(&self) -> Result<bool>;

}

impl TimeTracking for Entry {

    fn set_start_datetime(&mut self, dt: NaiveDateTime) -> Result<()> {
        let s = dt.format(DATE_TIME_FORMAT).to_string();

        self.get_header_mut()
            .insert(DATE_TIME_START_HEADER_PATH, Value::String(s))
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn get_start_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read(DATE_TIME_START_HEADER_PATH)
            .map_err_into(TTEK::HeaderReadError)
            .and_then(header_value_to_dt)
    }

    fn delete_start_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_START_HEADER_PATH)
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn set_end_datetime(&mut self, dt: NaiveDateTime) -> Result<()> {
        let s = dt.format(DATE_TIME_FORMAT).to_string();

        self.get_header_mut()
            .insert(DATE_TIME_END_HEADER_PATH, Value::String(s))
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn get_end_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read(DATE_TIME_END_HEADER_PATH)
            .map_err_into(TTEK::HeaderReadError)
            .and_then(header_value_to_dt)
    }

    fn delete_end_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_END_HEADER_PATH)
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    /// Check whether the Event is valid
    ///
    /// That is:
    ///
    /// * The end date is after the start date (or not set)
    ///
    /// # Return values
    ///
    /// Ok(true) if Event is valid
    /// Ok(false) if Event is invalid
    /// Err(e) if checking validity failed
    ///
    fn valid(&self) -> Result<bool> {
        self.get_start_datetime().and_then(|st| self.get_end_datetime().map(|et| st <= et))
    }

}

fn header_value_to_dt(val: Option<&Value>) -> Result<Option<NaiveDateTime>> {
    match val {
        Some(&Value::String(ref s)) => {
            NaiveDateTime::parse_from_str(s, DATE_TIME_FORMAT)
                .map_err_into(TTEK::DateTimeParserError)
                .map(Some)

        },
        Some(_) => Err(TTEK::HeaderFieldTypeError.into_error()),
        None => Ok(None),
    }
}

<commit_msg>Add TimeTracking::get_timetrack_tag()<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
//

//! Event types
//!
//! This module contains types that represent events. These types (which wrap FileLockEntries from
//! the Store) represent Events, thus they have functionality for settings the start and end-time,
//! getting the start and end time and also deleting start and end time.
//!

use chrono::naive::datetime::NaiveDateTime;

use libimagstore::store::Entry;
use libimagerror::into::IntoError;

use tag::TimeTrackingTag as TTT;
use error::TimeTrackErrorKind as TTEK;
use error::MapErrInto;
use result::Result;
use constants::*;

use toml::Value;
use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadExt;

pub trait TimeTracking {

    fn get_timetrack_tag(&self) -> Result<TTT>;

    fn set_start_datetime(&mut self, dt: NaiveDateTime) -> Result<()>;

    fn get_start_datetime(&self) -> Result<Option<NaiveDateTime>>;

    fn delete_start_datetime(&mut self) -> Result<()>;

    fn set_end_datetime(&mut self, dt: NaiveDateTime) -> Result<()>;

    fn get_end_datetime(&self) -> Result<Option<NaiveDateTime>>;

    fn delete_end_datetime(&mut self) -> Result<()>;

    fn valid(&self) -> Result<bool>;

}

impl TimeTracking for Entry {

    fn get_timetrack_tag(&self) -> Result<TTT> {
        self.get_header()
            .read(DATE_TIME_TAG_HEADER_PATH)
            .map_err_into(TTEK::HeaderReadError)
            .map(|value| match value {
                Some(&Value::String(ref s)) => s.clone().into(),
                _ => unimplemented!(),
            })
    }

    fn set_start_datetime(&mut self, dt: NaiveDateTime) -> Result<()> {
        let s = dt.format(DATE_TIME_FORMAT).to_string();

        self.get_header_mut()
            .insert(DATE_TIME_START_HEADER_PATH, Value::String(s))
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn get_start_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read(DATE_TIME_START_HEADER_PATH)
            .map_err_into(TTEK::HeaderReadError)
            .and_then(header_value_to_dt)
    }

    fn delete_start_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_START_HEADER_PATH)
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn set_end_datetime(&mut self, dt: NaiveDateTime) -> Result<()> {
        let s = dt.format(DATE_TIME_FORMAT).to_string();

        self.get_header_mut()
            .insert(DATE_TIME_END_HEADER_PATH, Value::String(s))
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    fn get_end_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read(DATE_TIME_END_HEADER_PATH)
            .map_err_into(TTEK::HeaderReadError)
            .and_then(header_value_to_dt)
    }

    fn delete_end_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_END_HEADER_PATH)
            .map_err_into(TTEK::HeaderWriteError)
            .map(|_| ())
    }

    /// Check whether the Event is valid
    ///
    /// That is:
    ///
    /// * The end date is after the start date (or not set)
    ///
    /// # Return values
    ///
    /// Ok(true) if Event is valid
    /// Ok(false) if Event is invalid
    /// Err(e) if checking validity failed
    ///
    fn valid(&self) -> Result<bool> {
        self.get_start_datetime().and_then(|st| self.get_end_datetime().map(|et| st <= et))
    }

}

fn header_value_to_dt(val: Option<&Value>) -> Result<Option<NaiveDateTime>> {
    match val {
        Some(&Value::String(ref s)) => {
            NaiveDateTime::parse_from_str(s, DATE_TIME_FORMAT)
                .map_err_into(TTEK::DateTimeParserError)
                .map(Some)

        },
        Some(_) => Err(TTEK::HeaderFieldTypeError.into_error()),
        None => Ok(None),
    }
}

<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
            .map(|_| ())
    }

}

<commit_msg>Write index after committing<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;

use libimagerror::into::IntoError;
use libimagerror::trace::trace_error;
use libimagstore::hook::Hook;
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::result::HookResult;
use libimagstore::storeid::StoreId;
use libimagutil::debug_result::*;

use vcs::git::error::GitHookError as GHE;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::MapErrInto;
use vcs::git::error::MapIntoHookError;
use vcs::git::result::Result;
use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    /// The implementation of the UpdateHook
    ///
    /// # Scope
    ///
    /// This hook takes the git index and commits it either interactively or with a default message,
    /// if there is no configuration for an interactive commit.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;
        use vcs::git::error::MapIntoHookError;
        use vcs::git::util::fetch_index;
        use git2::{Reference as GitReference, Repository, Error as Git2Error};
        use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

        debug!("[GIT UPDATE HOOK]: {:?}", id);

        let action    = StoreAction::Update;
        let cfg       = try!(self.runtime.config_value_or_err(&action));
        let repo      = try!(self.runtime.repository(&action));
        let mut index = try!(fetch_index(repo, &action));

        let signature = try!(
            repo.signature()
                .map_err_into(GHEK::MkSignature)
                .map_dbg_err_str("Failed to fetch signature")
                .map_into_hook_error()
        );

        let head = try!(
            repo.head()
                .map_err_into(GHEK::HeadFetchError)
                .map_dbg_err_str("Failed to fetch HEAD")
                .map_into_hook_error()
        );

        let file_status = try!(
            repo
                .status_file(id.local())
                .map_dbg_err_str("Failed to fetch file status")
                .map_dbg_err(|e| format!("\t->  {:?}", e))
                .map_err_into(GHEK::RepositoryFileStatusError)
                .map_into_hook_error()
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_NEW) || file_status.contains(STATUS_WT_MODIFIED) {
                debug!("[GIT CREATE HOOK]: File is modified/new: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[id.local()], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
                .map_dbg_err_str("Failed to add to index")
                .map_into_hook_error()
        );

        let tree_id = try!(
            index.write_tree()
                .map_err_into(GHEK::RepositoryIndexWritingError)
                .map_dbg_err_str("Failed to write tree")
                .map_into_hook_error()
        );

        let mut parents = Vec::new();
        {
            let commit = try!(
                repo.find_commit(head.target().unwrap())
                    .map_err_into(GHEK::RepositoryParentFetchingError)
                    .map_dbg_err_str("Failed to find commit HEAD")
                    .map_into_hook_error()
            );
            parents.push(commit);
        }

        // for converting from Vec<Commit> to Vec<&Commit>
        let parents = parents.iter().collect::<Vec<_>>();

        let tree = try!(
            repo.find_tree(tree_id)
                .map_err_into(GHEK::RepositoryParentFetchingError)
                .map_dbg_err_str("Failed to find tree")
                .map_into_hook_error()
        );

        let message = try!(commit_message(cfg, StoreAction::Update)
                .map_dbg_err_str("Failed to get commit message"));

        try!(repo.commit(Some("HEAD"), &signature, &signature, &message, &tree, &parents)
            .map_dbg_str("Committed")
            .map_dbg_err_str("Failed to commit")
            .map_err_into(GHEK::RepositoryCommittingError)
            .map_into_hook_error()
        );

        index.write()
            .map_err_into(GHEK::RepositoryIndexWritingError)
            .map_dbg_err_str("Failed to write tree")
            .map_into_hook_error()
            .map(|_| ())
    }

}

<|endoftext|>


<commit_before>#![allow(dead_code)] // stack_guard isn't used right now on all platforms

use crate::cell::RefCell;
use crate::sys::thread::guard::Guard;
use crate::thread::Thread;

struct ThreadInfo {
    stack_guard: Option<Guard>,
    thread: Thread,
}

thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }

impl ThreadInfo {
    fn with<R, F>(f: F) -> Option<R>
    where
        F: FnOnce(&mut ThreadInfo) -> R,
    {
        THREAD_INFO
            .try_with(move |thread_info| {
                let mut thread_info = thread_info.borrow_mut();
                let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
                    stack_guard: None,
                    thread: Thread::new(None),
                });
                f(thread_info)
            })
            .ok()
    }
}

pub fn current_thread() -> Option<Thread> {
    ThreadInfo::with(|info| info.thread.clone())
}

pub fn stack_guard() -> Option<Guard> {
    ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
}

pub fn set(stack_guard: Option<Guard>, thread: Thread) {
    THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
    THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { stack_guard, thread }));
}

pub fn reset_guard(stack_guard: Option<Guard>) {
    THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
}
<commit_msg>Use const {} for the THREAD_INFO thread local<commit_after>#![allow(dead_code)] // stack_guard isn't used right now on all platforms
#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny

use crate::cell::RefCell;
use crate::sys::thread::guard::Guard;
use crate::thread::Thread;

struct ThreadInfo {
    stack_guard: Option<Guard>,
    thread: Thread,
}

thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }

impl ThreadInfo {
    fn with<R, F>(f: F) -> Option<R>
    where
        F: FnOnce(&mut ThreadInfo) -> R,
    {
        THREAD_INFO
            .try_with(move |thread_info| {
                let mut thread_info = thread_info.borrow_mut();
                let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
                    stack_guard: None,
                    thread: Thread::new(None),
                });
                f(thread_info)
            })
            .ok()
    }
}

pub fn current_thread() -> Option<Thread> {
    ThreadInfo::with(|info| info.thread.clone())
}

pub fn stack_guard() -> Option<Guard> {
    ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
}

pub fn set(stack_guard: Option<Guard>, thread: Thread) {
    THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
    THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { stack_guard, thread }));
}

pub fn reset_guard(stack_guard: Option<Guard>) {
    THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
}
<|endoftext|>


<commit_before>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_exe().expect("Unable to find executable path.");
    asset_dir.pop();
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_exe().expect("Unable to find executable path.");
    asset_dir.pop();
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        if format!("{:?}", rsi) != "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }" {
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<commit_msg>Fixes the unit tests.<commit_after>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        if format!("{:?}", rsi) != "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }" {
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<|endoftext|>


<commit_before>use pulldown_cmark::{html, Options, Parser};
use std::fs;
use rss::ChannelBuilder;
use rss::Item;

mod paths;
mod printing;

fn main() {
    printing::heading("Creating the build directory for my website.");

    printing::subheading("Cleaning the build directory.");
    paths::remove_dir(&paths::build());

    printing::subheading("Generating the build directory.");
    paths::create_dir(&paths::build());
    generate_pages();

    printing::subheading("Configuring the build directory.");
    // Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
    paths::create_file(&paths::build().join("CNAME"), "trevordmiller.com");
}

fn generate_pages() {
    let mut rss_items = Vec::new();

    match fs::read_dir(&paths::pages()) {
        Ok(markdown_files) => {
            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);

                let mut lines = markdown_file_contents.lines();

                let title = match lines.next() {
                    Some(title) => title.replace("# ", ""),
                    None => panic!("Cannot find a title in {}.", &route),
                };

                match lines.next() {
                    Some(_) => (),
                    None => panic!("Cannot find a break between title and description in {}.", &route),
                };

                let description = match lines.next() {
                    Some(description) => description.replace("_", ""),
                    None => panic!("Cannot find a description in {}.", &route),
                };

                if route == "index" {
                    paths::create_file(
                        &paths::build().join("index.html"),
                        &markdown_to_html(&markdown_file_contents, &title, &description),
                    );
                } else {
                    paths::create_dir(&paths::build().join(route));
                    paths::create_file(
                        &paths::build().join(route).join("index.html"),
                        &markdown_to_html(&markdown_file_contents, &title, &description),
                    );
                }

                let mut rss_item = Item::default();
                rss_item.set_link(format!("https://trevordmiller.com/{}/", &route));
                rss_item.set_title(title);
                rss_item.set_description(description);

                rss_items.push(rss_item)
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    let channel = match ChannelBuilder::default()
        .language("en-us".to_string())
        .link("https://trevordmiller.com")
        .title("trevordmiller")
        .description("RSS feed for https://trevordmiller.com.")
        .items(rss_items)
        .build() {
            Ok(channel) => channel.to_string(),
            Err(error) => panic!("There was a problem: {:?}", error),
        };

    dbg!(&channel);

    paths::create_file(
        &paths::build().join("rss.xml"),
        &channel,
    );
}

fn markdown_to_html(markdown: &str, title: &str, description: &str) -> std::string::String {
    let css = r#"
        body {
            max-width: 80ch;
            margin: 0 auto;
            padding: 1rem 1rem 2rem 1rem;
            font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
            color: #333333;
        }
        code {
            overflow: auto;
            display: block;
            background: #f5f5f5;
        }
        nav a, li, code {
            padding: 0.5rem;
        }
        header, code {
            font-family: "Courier New", Courier, monospace;
        }
        main {
            margin-top: 1rem;
            border-top: 1px solid #d3d3d3;
        }
        header, nav {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
        }
        header::before {
            content: ">";
            margin: 0 1ch 0 0;
        }
        header::after {
            content: "";
            margin: 0 0 0 1ch;
            display: inline-block;
            background: #333333;
            width: 1ch;
            height: 1em;
            animation: cursor 800ms infinite;
        }
        @keyframes cursor {
            0% {
                opacity: 0;
            }
            50% {
                opacity: 1;
            }
            to {
                opacity: 0;
            }
        }
    "#;

    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!("
        <!DOCTYPE html>

                <title>trevordmiller | {}</title>
                <meta name='description' content='{}'>
                <meta name='author' content='Trevor D. Miller'>
                <meta charset='utf-8'>
                <meta name='viewport' content='width=device-width, initial-scale=1'>

                <link rel='alternate' type='application/rss+xml' title='trevordmiller RSS Feed' href='/rss.xml'>

                trevordmiller

                    Articles
                    About
                    Resume
                    Projects
                    RSS

                    {}

    ",
        &title,
        &description,
        &css,
        &html
    )
}
<commit_msg>Filter RSS feed to only individual articles<commit_after>use pulldown_cmark::{html, Options, Parser};
use std::fs;
use rss::ChannelBuilder;
use rss::Item;

mod paths;
mod printing;

fn main() {
    printing::heading("Creating the build directory for my website.");

    printing::subheading("Cleaning the build directory.");
    paths::remove_dir(&paths::build());

    printing::subheading("Generating the build directory.");
    paths::create_dir(&paths::build());
    generate_pages();

    printing::subheading("Configuring the build directory.");
    // Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
    paths::create_file(&paths::build().join("CNAME"), "trevordmiller.com");
}

fn generate_pages() {
    let mut rss_items = Vec::new();

    match fs::read_dir(&paths::pages()) {
        Ok(markdown_files) => {
            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);

                let mut lines = markdown_file_contents.lines();

                let title = match lines.next() {
                    Some(title) => title.replace("# ", ""),
                    None => panic!("Cannot find a title in {}.", &route),
                };

                match lines.next() {
                    Some(_) => (),
                    None => panic!("Cannot find a break between title and description in {}.", &route),
                };

                let description = match lines.next() {
                    Some(description) => description.replace("_", ""),
                    None => panic!("Cannot find a description in {}.", &route),
                };

                if route == "index" {
                    paths::create_file(
                        &paths::build().join("index.html"),
                        &markdown_to_html(&markdown_file_contents, &title, &description),
                    );
                } else {
                    paths::create_dir(&paths::build().join(route));
                    paths::create_file(
                        &paths::build().join(route).join("index.html"),
                        &markdown_to_html(&markdown_file_contents, &title, &description),
                    );
                }

                let is_individual_article = route != "index" && route != "about" && route != "resume" && route != "projects";

                if is_individual_article {
                    let mut rss_item = Item::default();
                    rss_item.set_link(format!("https://trevordmiller.com/{}/", &route));
                    rss_item.set_title(title);
                    rss_item.set_description(description);
                    rss_items.push(rss_item)
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    let channel = match ChannelBuilder::default()
        .language("en-us".to_string())
        .link("https://trevordmiller.com")
        .title("trevordmiller")
        .description("RSS feed of articles from https://trevordmiller.com.")
        .items(rss_items)
        .build() {
            Ok(channel) => channel.to_string(),
            Err(error) => panic!("There was a problem: {:?}", error),
        };

    paths::create_file(
        &paths::build().join("rss.xml"),
        &channel,
    );
}

fn markdown_to_html(markdown: &str, title: &str, description: &str) -> std::string::String {
    let css = r#"
        body {
            max-width: 80ch;
            margin: 0 auto;
            padding: 1rem 1rem 2rem 1rem;
            font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
            color: #333333;
        }
        code {
            overflow: auto;
            display: block;
            background: #f5f5f5;
        }
        nav a, li, code {
            padding: 0.5rem;
        }
        header, code {
            font-family: "Courier New", Courier, monospace;
        }
        main {
            margin-top: 1rem;
            border-top: 1px solid #d3d3d3;
        }
        header, nav {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
        }
        header::before {
            content: ">";
            margin: 0 1ch 0 0;
        }
        header::after {
            content: "";
            margin: 0 0 0 1ch;
            display: inline-block;
            background: #333333;
            width: 1ch;
            height: 1em;
            animation: cursor 800ms infinite;
        }
        @keyframes cursor {
            0% {
                opacity: 0;
            }
            50% {
                opacity: 1;
            }
            to {
                opacity: 0;
            }
        }
    "#;

    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!("
        <!DOCTYPE html>

                <title>trevordmiller | {}</title>
                <meta name='description' content='{}'>
                <meta name='author' content='Trevor D. Miller'>
                <meta charset='utf-8'>
                <meta name='viewport' content='width=device-width, initial-scale=1'>

                <link rel='alternate' type='application/rss+xml' title='trevordmiller RSS Feed' href='/rss.xml'>

                trevordmiller

                    Articles
                    About
                    Resume
                    Projects
                    RSS

                    {}

    ",
        &title,
        &description,
        &css,
        &html
    )
}
<|endoftext|>


<commit_before>extern crate noise_protocol as noise;
extern crate libsodium_sys;
extern crate sodiumoxide;

// TODO Add AEADs, after
// [https://github.com/dnaq/sodiumoxide/pull/149] is merged.

// TODO Use stream hasher, after this is fixed:
// [https://github.com/dnaq/sodiumoxide/issues/119]

use libsodium_sys::crypto_generichash_blake2b;
use noise::*;
use sodiumoxide::crypto::hash::{sha256, sha512};
use sodiumoxide::crypto::scalarmult::curve25519;
use sodiumoxide::init as sodium_init;
use sodiumoxide::randombytes::randombytes_into;
use sodiumoxide::utils::memzero;
use std::mem::uninitialized;
use std::ptr::null;

/// Sodiumoxide init.
///
/// This will make some operations potentially faster, and make `genkey` thread safe.
pub fn init() {
    sodium_init();
}

pub struct X25519Key(curve25519::Scalar);

impl U8Array for X25519Key {
    fn new() -> Self {
        X25519Key(curve25519::Scalar([0u8; 32]))
    }

    fn new_with(v: u8) -> Self {
        X25519Key(curve25519::Scalar([v; 32]))
    }

    fn from_slice(s: &[u8]) -> Self {
        X25519Key(curve25519::Scalar::from_slice(s).unwrap())
    }

    fn len() -> usize {
        32
    }

    fn as_slice(&self) -> &[u8] {
        & (self.0).0
    }

    fn as_mut(&mut self) -> &mut [u8] {
        &mut (self.0).0
    }
}

pub struct Sensitive(A);

impl Drop for Sensitive
    where A: U8Array
{
    fn drop(&mut self) {
        memzero(self.0.as_mut())
    }
}

impl U8Array for Sensitive
    where A: U8Array
{
    fn new() -> Self {
        Sensitive(A::new())
    }

    fn new_with(v: u8) -> Self {
        Sensitive(A::new_with(v))
    }

    fn from_slice(s: &[u8]) -> Self {
        Sensitive(A::from_slice(s))
    }

    fn len() -> usize {
        A::len()
    }

    fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    fn as_mut(&mut self) -> &mut [u8] {
        self.0.as_mut()
    }
}

pub enum X25519 {}

#[derive(Default)]
pub struct Sha256 {
    buf: Vec<u8>,
}

#[derive(Default)]
pub struct Sha512 {
    buf: Vec<u8>,
}

pub struct Blake2b {
    // TODO: 64-byte alignment.
    // crypto_generichash_statebytes() is 384, as of libsodium 1.0.8.
    state: [u8; 384],
}

#[cfg(test)]
#[test]
fn test_blake2b_state_size() {
    assert!(::std::mem::size_of::<Blake2b>() >= unsafe { crypto_generichash_statebytes() });
}

impl DH for X25519 {
    type Key = X25519Key;
    type Pubkey = [u8; 32];
    type Output = Sensitive<[u8; 32]>;

    fn name() -> &'static str {
        "25519"
    }

    fn genkey() -> Self::Key {
        let mut k = [0u8; 32];
        randombytes_into(&mut k);
        k[0] &= 248;
        k[31] &= 127;
        k[31] |= 64;
        X25519Key(curve25519::Scalar(k))
    }

    fn pubkey(k: &Self::Key) -> Self::Pubkey {
        curve25519::scalarmult_base(&k.0).0
    }

    fn dh(k: &Self::Key, pk: &Self::Pubkey) -> Self::Output {
        let pk = curve25519::GroupElement(*pk);
        // Libsodium returns error when DH result is all-zero, but noise explicitly permits that.
        // See section 9.1 of the spec:
        // http://www.noiseprotocol.org/noise.html#dummy-static-public-keys
        Sensitive(curve25519::scalarmult(&k.0, &pk).map(|x| x.0).unwrap_or([0u8; 32]))
    }
}

impl Hash for Sha256 {
    type Block = [u8; 64];
    type Output = Sensitive<[u8; 32]>;

    fn name() -> &'static str {
        "SHA256"
    }

    fn input(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }

    fn result(&mut self) -> Self::Output {
        Sensitive(sha256::hash(&self.buf).0)
    }
}

impl Hash for Sha512 {
    type Block = [u8; 128];
    type Output = Sensitive<[u8; 64]>;

    fn name() -> &'static str {
        "SHA512"
    }

    fn input(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }

    fn result(&mut self) -> Self::Output {
        Sensitive(sha512::hash(&self.buf).0)
    }
}

impl Default for Blake2b {
    fn default() -> Self {
        unsafe {
            let mut b: Blake2b = uninitialized();
            crypto_generichash_init(b.state.as_mut_ptr() as *mut _,
                                    null(), 0,
                                    64);
            b
        }
    }
}

impl Hash for Blake2b {
    type Block = [u8; 128];
    type Output = Sensitive<[u8; 64]>;

    fn name() -> &'static str {
        "BLAKE2b"
    }

    fn input(&mut self, data: &[u8]) {
        unsafe {
            crypto_generichash_update(self.state.as_mut_ptr() as *mut _,
                                      data.as_ptr(),
                                      data.len() as u64);
        }
    }

    fn result(&mut self) -> Self::Output {
        unsafe {
            let mut out: Self::Output = uninitialized();
            crypto_generichash_final(self.state.as_mut_ptr() as *mut _,
                                     out.as_mut().as_mut_ptr(), 64);
            out
        }
    }
}
<commit_msg>Fix sodiumoxide<commit_after>extern crate noise_protocol as noise;
extern crate libsodium_sys;
extern crate sodiumoxide;

// TODO Add AEADs, after
// [https://github.com/dnaq/sodiumoxide/pull/149] is merged.

// TODO Use stream hasher, after this is fixed:
// [https://github.com/dnaq/sodiumoxide/issues/119]

use libsodium_sys::*;
use noise::*;
use sodiumoxide::crypto::hash::{sha256, sha512};
use sodiumoxide::crypto::scalarmult::curve25519;
use sodiumoxide::init as sodium_init;
use sodiumoxide::randombytes::randombytes_into;
use sodiumoxide::utils::memzero;
use std::mem::uninitialized;
use std::ptr::null;

/// Sodiumoxide init.
///
/// This will make some operations potentially faster, and make `genkey` thread safe.
pub fn init() {
    sodium_init();
}

pub struct X25519Key(curve25519::Scalar);

impl U8Array for X25519Key {
    fn new() -> Self {
        X25519Key(curve25519::Scalar([0u8; 32]))
    }

    fn new_with(v: u8) -> Self {
        X25519Key(curve25519::Scalar([v; 32]))
    }

    fn from_slice(s: &[u8]) -> Self {
        X25519Key(curve25519::Scalar::from_slice(s).unwrap())
    }

    fn len() -> usize {
        32
    }

    fn as_slice(&self) -> &[u8] {
        & (self.0).0
    }

    fn as_mut(&mut self) -> &mut [u8] {
        &mut (self.0).0
    }
}

pub struct Sensitive(A);

impl Drop for Sensitive
    where A: U8Array
{
    fn drop(&mut self) {
        memzero(self.0.as_mut())
    }
}

impl U8Array for Sensitive
    where A: U8Array
{
    fn new() -> Self {
        Sensitive(A::new())
    }

    fn new_with(v: u8) -> Self {
        Sensitive(A::new_with(v))
    }

    fn from_slice(s: &[u8]) -> Self {
        Sensitive(A::from_slice(s))
    }

    fn len() -> usize {
        A::len()
    }

    fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    fn as_mut(&mut self) -> &mut [u8] {
        self.0.as_mut()
    }
}

pub enum X25519 {}

#[derive(Default)]
pub struct Sha256 {
    buf: Vec<u8>,
}

#[derive(Default)]
pub struct Sha512 {
    buf: Vec<u8>,
}

pub struct Blake2b {
    // TODO: 64-byte alignment.
    // crypto_generichash_statebytes() is 384, as of libsodium 1.0.8.
    state: [u8; 384],
}

#[cfg(test)]
#[test]
fn test_blake2b_state_size() {
    assert!(::std::mem::size_of::<Blake2b>() >= unsafe { crypto_generichash_statebytes() });
}

impl DH for X25519 {
    type Key = X25519Key;
    type Pubkey = [u8; 32];
    type Output = Sensitive<[u8; 32]>;

    fn name() -> &'static str {
        "25519"
    }

    fn genkey() -> Self::Key {
        let mut k = [0u8; 32];
        randombytes_into(&mut k);
        k[0] &= 248;
        k[31] &= 127;
        k[31] |= 64;
        X25519Key(curve25519::Scalar(k))
    }

    fn pubkey(k: &Self::Key) -> Self::Pubkey {
        curve25519::scalarmult_base(&k.0).0
    }

    fn dh(k: &Self::Key, pk: &Self::Pubkey) -> Self::Output {
        let pk = curve25519::GroupElement(*pk);
        // Libsodium returns error when DH result is all-zero, but noise explicitly permits that.
        // See section 9.1 of the spec:
        // http://www.noiseprotocol.org/noise.html#dummy-static-public-keys
        Sensitive(curve25519::scalarmult(&k.0, &pk).map(|x| x.0).unwrap_or([0u8; 32]))
    }
}

impl Hash for Sha256 {
    type Block = [u8; 64];
    type Output = Sensitive<[u8; 32]>;

    fn name() -> &'static str {
        "SHA256"
    }

    fn input(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }

    fn result(&mut self) -> Self::Output {
        Sensitive(sha256::hash(&self.buf).0)
    }
}

impl Hash for Sha512 {
    type Block = [u8; 128];
    type Output = Sensitive<[u8; 64]>;

    fn name() -> &'static str {
        "SHA512"
    }

    fn input(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }

    fn result(&mut self) -> Self::Output {
        Sensitive(sha512::hash(&self.buf).0)
    }
}

impl Default for Blake2b {
    fn default() -> Self {
        unsafe {
            let mut b: Blake2b = uninitialized();
            crypto_generichash_init(b.state.as_mut_ptr() as *mut _,
                                    null(), 0,
                                    64);
            b
        }
    }
}

impl Hash for Blake2b {
    type Block = [u8; 128];
    type Output = Sensitive<[u8; 64]>;

    fn name() -> &'static str {
        "BLAKE2b"
    }

    fn input(&mut self, data: &[u8]) {
        unsafe {
            crypto_generichash_update(self.state.as_mut_ptr() as *mut _,
                                      data.as_ptr(),
                                      data.len() as u64);
        }
    }

    fn result(&mut self) -> Self::Output {
        unsafe {
            let mut out: Self::Output = uninitialized();
            crypto_generichash_final(self.state.as_mut_ptr() as *mut _,
                                     out.as_mut().as_mut_ptr(), 64);
            out
        }
    }
}
<|endoftext|>