<commit_before>//! There are many AstNodes, but only a few tokens, so we hand-write them here.

use std::convert::{TryFrom, TryInto};

use crate::{
    ast::{AstToken, Comment, RawString, String, Whitespace},
    TextRange, TextSize,
};
use rustc_lexer::unescape::{unescape_literal, Mode};

impl Comment {
    pub fn kind(&self) -> CommentKind {
        kind_by_prefix(self.text())
    }

    pub fn prefix(&self) -> &'static str {
        for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
            if *k == self.kind() && self.text().starts_with(prefix) {
                return prefix;
            }
        }
        unreachable!()
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct CommentKind {
    pub shape: CommentShape,
    pub doc: Option<CommentPlacement>,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CommentShape {
    Line,
    Block,
}

impl CommentShape {
    pub fn is_line(self) -> bool {
        self == CommentShape::Line
    }

    pub fn is_block(self) -> bool {
        self == CommentShape::Block
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CommentPlacement {
    Inner,
    Outer,
}

const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
    use {CommentPlacement::*, CommentShape::*};
    &[
        ("////", CommentKind { shape: Line, doc: None }),
        ("///", CommentKind { shape: Line, doc: Some(Outer) }),
        ("//!", CommentKind { shape: Line, doc: Some(Inner) }),
        ("/**", CommentKind { shape: Block, doc: Some(Outer) }),
        ("/*!", CommentKind { shape: Block, doc: Some(Inner) }),
        ("//", CommentKind { shape: Line, doc: None }),
        ("/*", CommentKind { shape: Block, doc: None }),
    ]
};

fn kind_by_prefix(text: &str) -> CommentKind {
    if text == "/**/" {
        return CommentKind { shape: CommentShape::Block, doc: None };
    }
    for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() {
        if text.starts_with(prefix) {
            return *kind;
        }
    }
    panic!("bad comment text: {:?}", text)
}

impl Whitespace {
    pub fn spans_multiple_lines(&self) -> bool {
        let text = self.text();
        text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
    }
}

pub struct QuoteOffsets {
    pub quotes: [TextRange; 2],
    pub contents: TextRange,
}

impl QuoteOffsets {
    fn new(literal: &str) -> Option<QuoteOffsets> {
        let left_quote = literal.find('"')?;
        let right_quote = literal.rfind('"')?;
        if left_quote == right_quote {
            // `literal` only contains one quote
            return None;
        }

        let start = TextSize::from(0);
        let left_quote = TextSize::try_from(left_quote).unwrap() + TextSize::of('"');
        let right_quote = TextSize::try_from(right_quote).unwrap();
        let end = TextSize::of(literal);

        let res = QuoteOffsets {
            quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)],
            contents: TextRange::new(left_quote, right_quote),
        };
        Some(res)
    }
}

pub trait HasQuotes: AstToken {
    fn quote_offsets(&self) -> Option<QuoteOffsets> {
        let text = self.text().as_str();
        let offsets = QuoteOffsets::new(text)?;
        let o = self.syntax().text_range().start();
        let offsets = QuoteOffsets {
            quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o],
            contents: offsets.contents + o,
        };
        Some(offsets)
    }
    fn open_quote_text_range(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.quotes[0])
    }

    fn close_quote_text_range(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.quotes[1])
    }

    fn text_range_between_quotes(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.contents)
    }
}

impl HasQuotes for String {}
impl HasQuotes for RawString {}

pub trait HasStringValue: HasQuotes {
    fn value(&self) -> Option<std::string::String>;
}

impl HasStringValue for String {
    fn value(&self) -> Option<std::string::String> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];

        let mut buf = std::string::String::with_capacity(text.len());
        let mut has_error = false;
        unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
            Ok(c) => buf.push(c),
            Err(_) => has_error = true,
        });

        if has_error {
            return None;
        }
        Some(buf)
    }
}

impl HasStringValue for RawString {
    fn value(&self) -> Option<std::string::String> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        Some(text.to_string())
    }
}

impl RawString {
    pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
        let contents_range = self.text_range_between_quotes()?;
        assert!(TextRange::up_to(contents_range.len()).contains_range(range));
        Some(range + contents_range.start())
    }
}

#[derive(Debug)]
pub enum FormatSpecifier {
    Open,
    Close,
    Integer,
    Identifier,
    Colon,
    Fill,
    Align,
    Sign,
    NumberSign,
    Zero,
    DollarSign,
    Dot,
    Asterisk,
    QuestionMark,
}

pub trait HasFormatSpecifier: AstToken {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>>;

    fn lex_format_specifier<F>(&self, mut callback: F)
    where
        F: FnMut(TextRange, FormatSpecifier),
    {
        let char_ranges = if let Some(char_ranges) = self.char_ranges() {
            char_ranges
        } else {
            return;
        };
        let mut chars = char_ranges.iter().peekable();

        while let Some((range, first_char)) = chars.next() {
            match first_char {
                Ok('{') => {
                    // Format specifier, see syntax at https://doc.rust-lang.org/std/fmt/index.html#syntax
                    if let Some((_, Ok('{'))) = chars.peek() {
                        // Escaped format specifier, `{{`
                        chars.next();
                        continue;
                    }

                    callback(*range, FormatSpecifier::Open);

                    // check for integer/identifier
                    match chars
                        .peek()
                        .and_then(|next| next.1.as_ref().ok())
                        .copied()
                        .unwrap_or_default()
                    {
                        '0'..='9' => {
                            // integer
                            read_integer(&mut chars, &mut callback);
                        }
                        c if c == '_' || c.is_alphabetic() => {
                            // identifier
                            read_identifier(&mut chars, &mut callback);
                        }
                        _ => {}
                    }

                    if let Some((_, Ok(':'))) = chars.peek() {
                        skip_char_and_emit(&mut chars, FormatSpecifier::Colon, &mut callback);

                        // check for fill/align
                        let mut cloned = chars.clone().take(2);
                        let first = cloned
                            .next()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default();
                        let second = cloned
                            .next()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default();
                        match second {
                            '<' | '^' | '>' => {
                                // alignment specifier, first char specifies fillment
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Fill,
                                    &mut callback,
                                );
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Align,
                                    &mut callback,
                                );
                            }
                            _ => match first {
                                '<' | '^' | '>' => {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::Align,
                                        &mut callback,
                                    );
                                }
                                _ => {}
                            },
                        }

                        // check for sign
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '+' | '-' => {
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Sign,
                                    &mut callback,
                                );
                            }
                            _ => {}
                        }

                        // check for `#`
                        if let Some((_, Ok('#'))) = chars.peek() {
                            skip_char_and_emit(
                                &mut chars,
                                FormatSpecifier::NumberSign,
                                &mut callback,
                            );
                        }

                        // check for `0`
                        let mut cloned = chars.clone().take(2);
                        let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                        let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();

                        if first == Some('0') && second != Some('$') {
                            skip_char_and_emit(&mut chars, FormatSpecifier::Zero, &mut callback);
                        }

                        // width
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '0'..='9' => {
                                read_integer(&mut chars, &mut callback);
                                if let Some((_, Ok('$'))) = chars.peek() {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::DollarSign,
                                        &mut callback,
                                    );
                                }
                            }
                            c if c == '_' || c.is_alphabetic() => {
                                read_identifier(&mut chars, &mut callback);
                                if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
                                    != Some('$')
                                {
                                    continue;
                                }
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::DollarSign,
                                    &mut callback,
                                );
                            }
                            _ => {}
                        }

                        // precision
                        if let Some((_, Ok('.'))) = chars.peek() {
                            skip_char_and_emit(&mut chars, FormatSpecifier::Dot, &mut callback);

                            match chars
                                .peek()
                                .and_then(|next| next.1.as_ref().ok())
                                .copied()
                                .unwrap_or_default()
                            {
                                '*' => {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::Asterisk,
                                        &mut callback,
                                    );
                                }
                                '0'..='9' => {
                                    read_integer(&mut chars, &mut callback);
                                    if let Some((_, Ok('$'))) = chars.peek() {
                                        skip_char_and_emit(
                                            &mut chars,
                                            FormatSpecifier::DollarSign,
                                            &mut callback,
                                        );
                                    }
                                }
                                c if c == '_' || c.is_alphabetic() => {
                                    read_identifier(&mut chars, &mut callback);
                                    if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
                                        != Some('$')
                                    {
                                        continue;
                                    }
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::DollarSign,
                                        &mut callback,
                                    );
                                }
                                _ => {
                                    continue;
                                }
                            }
                        }

                        // type
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '?' => {
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::QuestionMark,
                                    &mut callback,
                                );
                            }
                            c if c == '_' || c.is_alphabetic() => {
                                read_identifier(&mut chars, &mut callback);
                            }
                            _ => {}
                        }
                    }

                    let mut cloned = chars.clone().take(2);
                    let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                    let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                    if first != Some('}') {
                        continue;
                    }
                    if second == Some('}') {
                        // Escaped format end specifier, `}}`
                        continue;
                    }
                    skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
                }
                _ => {
                    while let Some((_, Ok(next_char))) = chars.peek() {
                        match next_char {
                            '{' => break,
                            _ => {}
                        }
                        chars.next();
                    }
                }
            };
        }

        fn skip_char_and_emit<'a, I, F>(
            chars: &mut std::iter::Peekable<I>,
            emit: FormatSpecifier,
            callback: &mut F,
        ) where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (range, _) = chars.next().unwrap();
            callback(*range, emit);
        }

        fn read_integer<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
        where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (mut range, c) = chars.next().unwrap();
            assert!(c.as_ref().unwrap().is_ascii_digit());
            while let Some((r, Ok(next_char))) = chars.peek() {
                if next_char.is_ascii_digit() {
                    chars.next();
                    range = range.cover(*r);
                } else {
                    break;
                }
            }
            callback(range, FormatSpecifier::Integer);
        }

        fn read_identifier<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
        where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (mut range, c) = chars.next().unwrap();
            assert!(c.as_ref().unwrap().is_alphabetic() || *c.as_ref().unwrap() == '_');
            while let Some((r, Ok(next_char))) = chars.peek() {
                if *next_char == '_' || next_char.is_ascii_digit() || next_char.is_alphabetic() {
                    chars.next();
                    range = range.cover(*r);
                } else {
                    break;
                }
            }
            callback(range, FormatSpecifier::Identifier);
        }
    }
}

impl HasFormatSpecifier for String {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();

        let mut res = Vec::with_capacity(text.len());
        unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
            res.push((
                TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
                    + offset,
                unescaped_char,
            ))
        });

        Some(res)
    }
}

impl HasFormatSpecifier for RawString {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();

        let mut res = Vec::with_capacity(text.len());
        for (idx, c) in text.char_indices() {
            res.push((TextRange::at(idx.try_into().unwrap(), TextSize::of(c)) + offset, Ok(c)));
        }
        Some(res)
    }
}
<commit_msg>Fix problem with format string tokenization<commit_after>//! There are many AstNodes, but only a few tokens, so we hand-write them here.

use std::convert::{TryFrom, TryInto};

use crate::{
    ast::{AstToken, Comment, RawString, String, Whitespace},
    TextRange, TextSize,
};
use rustc_lexer::unescape::{unescape_literal, Mode};

impl Comment {
    pub fn kind(&self) -> CommentKind {
        kind_by_prefix(self.text())
    }

    pub fn prefix(&self) -> &'static str {
        for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
            if *k == self.kind() && self.text().starts_with(prefix) {
                return prefix;
            }
        }
        unreachable!()
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct CommentKind {
    pub shape: CommentShape,
    pub doc: Option<CommentPlacement>,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CommentShape {
    Line,
    Block,
}

impl CommentShape {
    pub fn is_line(self) -> bool {
        self == CommentShape::Line
    }

    pub fn is_block(self) -> bool {
        self == CommentShape::Block
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CommentPlacement {
    Inner,
    Outer,
}

const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
    use {CommentPlacement::*, CommentShape::*};
    &[
        ("////", CommentKind { shape: Line, doc: None }),
        ("///", CommentKind { shape: Line, doc: Some(Outer) }),
        ("//!", CommentKind { shape: Line, doc: Some(Inner) }),
        ("/**", CommentKind { shape: Block, doc: Some(Outer) }),
        ("/*!", CommentKind { shape: Block, doc: Some(Inner) }),
        ("//", CommentKind { shape: Line, doc: None }),
        ("/*", CommentKind { shape: Block, doc: None }),
    ]
};

fn kind_by_prefix(text: &str) -> CommentKind {
    if text == "/**/" {
        return CommentKind { shape: CommentShape::Block, doc: None };
    }
    for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() {
        if text.starts_with(prefix) {
            return *kind;
        }
    }
    panic!("bad comment text: {:?}", text)
}

impl Whitespace {
    pub fn spans_multiple_lines(&self) -> bool {
        let text = self.text();
        text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
    }
}

pub struct QuoteOffsets {
    pub quotes: [TextRange; 2],
    pub contents: TextRange,
}

impl QuoteOffsets {
    fn new(literal: &str) -> Option<QuoteOffsets> {
        let left_quote = literal.find('"')?;
        let right_quote = literal.rfind('"')?;
        if left_quote == right_quote {
            // `literal` only contains one quote
            return None;
        }

        let start = TextSize::from(0);
        let left_quote = TextSize::try_from(left_quote).unwrap() + TextSize::of('"');
        let right_quote = TextSize::try_from(right_quote).unwrap();
        let end = TextSize::of(literal);

        let res = QuoteOffsets {
            quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)],
            contents: TextRange::new(left_quote, right_quote),
        };
        Some(res)
    }
}

pub trait HasQuotes: AstToken {
    fn quote_offsets(&self) -> Option<QuoteOffsets> {
        let text = self.text().as_str();
        let offsets = QuoteOffsets::new(text)?;
        let o = self.syntax().text_range().start();
        let offsets = QuoteOffsets {
            quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o],
            contents: offsets.contents + o,
        };
        Some(offsets)
    }
    fn open_quote_text_range(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.quotes[0])
    }

    fn close_quote_text_range(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.quotes[1])
    }

    fn text_range_between_quotes(&self) -> Option<TextRange> {
        self.quote_offsets().map(|it| it.contents)
    }
}

impl HasQuotes for String {}
impl HasQuotes for RawString {}

pub trait HasStringValue: HasQuotes {
    fn value(&self) -> Option<std::string::String>;
}

impl HasStringValue for String {
    fn value(&self) -> Option<std::string::String> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];

        let mut buf = std::string::String::with_capacity(text.len());
        let mut has_error = false;
        unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
            Ok(c) => buf.push(c),
            Err(_) => has_error = true,
        });

        if has_error {
            return None;
        }
        Some(buf)
    }
}

impl HasStringValue for RawString {
    fn value(&self) -> Option<std::string::String> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        Some(text.to_string())
    }
}

impl RawString {
    pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
        let contents_range = self.text_range_between_quotes()?;
        assert!(TextRange::up_to(contents_range.len()).contains_range(range));
        Some(range + contents_range.start())
    }
}

#[derive(Debug)]
pub enum FormatSpecifier {
    Open,
    Close,
    Integer,
    Identifier,
    Colon,
    Fill,
    Align,
    Sign,
    NumberSign,
    Zero,
    DollarSign,
    Dot,
    Asterisk,
    QuestionMark,
}

pub trait HasFormatSpecifier: AstToken {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>>;

    fn lex_format_specifier<F>(&self, mut callback: F)
    where
        F: FnMut(TextRange, FormatSpecifier),
    {
        let char_ranges = if let Some(char_ranges) = self.char_ranges() {
            char_ranges
        } else {
            return;
        };
        let mut chars = char_ranges.iter().peekable();

        while let Some((range, first_char)) = chars.next() {
            match first_char {
                Ok('{') => {
                    // Format specifier, see syntax at https://doc.rust-lang.org/std/fmt/index.html#syntax
                    if let Some((_, Ok('{'))) = chars.peek() {
                        // Escaped format specifier, `{{`
                        chars.next();
                        continue;
                    }

                    callback(*range, FormatSpecifier::Open);

                    // check for integer/identifier
                    match chars
                        .peek()
                        .and_then(|next| next.1.as_ref().ok())
                        .copied()
                        .unwrap_or_default()
                    {
                        '0'..='9' => {
                            // integer
                            read_integer(&mut chars, &mut callback);
                        }
                        c if c == '_' || c.is_alphabetic() => {
                            // identifier
                            read_identifier(&mut chars, &mut callback);
                        }
                        _ => {}
                    }

                    if let Some((_, Ok(':'))) = chars.peek() {
                        skip_char_and_emit(&mut chars, FormatSpecifier::Colon, &mut callback);

                        // check for fill/align
                        let mut cloned = chars.clone().take(2);
                        let first = cloned
                            .next()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default();
                        let second = cloned
                            .next()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default();
                        match second {
                            '<' | '^' | '>' => {
                                // alignment specifier, first char specifies fillment
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Fill,
                                    &mut callback,
                                );
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Align,
                                    &mut callback,
                                );
                            }
                            _ => match first {
                                '<' | '^' | '>' => {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::Align,
                                        &mut callback,
                                    );
                                }
                                _ => {}
                            },
                        }

                        // check for sign
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '+' | '-' => {
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::Sign,
                                    &mut callback,
                                );
                            }
                            _ => {}
                        }

                        // check for `#`
                        if let Some((_, Ok('#'))) = chars.peek() {
                            skip_char_and_emit(
                                &mut chars,
                                FormatSpecifier::NumberSign,
                                &mut callback,
                            );
                        }

                        // check for `0`
                        let mut cloned = chars.clone().take(2);
                        let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                        let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();

                        if first == Some('0') && second != Some('$') {
                            skip_char_and_emit(&mut chars, FormatSpecifier::Zero, &mut callback);
                        }

                        // width
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '0'..='9' => {
                                read_integer(&mut chars, &mut callback);
                                if let Some((_, Ok('$'))) = chars.peek() {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::DollarSign,
                                        &mut callback,
                                    );
                                }
                            }
                            c if c == '_' || c.is_alphabetic() => {
                                read_identifier(&mut chars, &mut callback);
                                if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
                                    != Some('$')
                                {
                                    continue;
                                }
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::DollarSign,
                                    &mut callback,
                                );
                            }
                            _ => {}
                        }

                        // precision
                        if let Some((_, Ok('.'))) = chars.peek() {
                            skip_char_and_emit(&mut chars, FormatSpecifier::Dot, &mut callback);

                            match chars
                                .peek()
                                .and_then(|next| next.1.as_ref().ok())
                                .copied()
                                .unwrap_or_default()
                            {
                                '*' => {
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::Asterisk,
                                        &mut callback,
                                    );
                                }
                                '0'..='9' => {
                                    read_integer(&mut chars, &mut callback);
                                    if let Some((_, Ok('$'))) = chars.peek() {
                                        skip_char_and_emit(
                                            &mut chars,
                                            FormatSpecifier::DollarSign,
                                            &mut callback,
                                        );
                                    }
                                }
                                c if c == '_' || c.is_alphabetic() => {
                                    read_identifier(&mut chars, &mut callback);
                                    if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
                                        != Some('$')
                                    {
                                        continue;
                                    }
                                    skip_char_and_emit(
                                        &mut chars,
                                        FormatSpecifier::DollarSign,
                                        &mut callback,
                                    );
                                }
                                _ => {
                                    continue;
                                }
                            }
                        }

                        // type
                        match chars
                            .peek()
                            .and_then(|next| next.1.as_ref().ok())
                            .copied()
                            .unwrap_or_default()
                        {
                            '?' => {
                                skip_char_and_emit(
                                    &mut chars,
                                    FormatSpecifier::QuestionMark,
                                    &mut callback,
                                );
                            }
                            c if c == '_' || c.is_alphabetic() => {
                                read_identifier(&mut chars, &mut callback);
                            }
                            _ => {}
                        }
                    }

                    let mut cloned = chars.clone().take(2);
                    let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                    if first != Some('}') {
                        continue;
                    }
                    skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
                }
                _ => {
                    while let Some((_, Ok(next_char))) = chars.peek() {
                        match next_char {
                            '{' => break,
                            _ => {}
                        }
                        chars.next();
                    }
                }
            };
        }

        fn skip_char_and_emit<'a, I, F>(
            chars: &mut std::iter::Peekable<I>,
            emit: FormatSpecifier,
            callback: &mut F,
        ) where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (range, _) = chars.next().unwrap();
            callback(*range, emit);
        }

        fn read_integer<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
        where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (mut range, c) = chars.next().unwrap();
            assert!(c.as_ref().unwrap().is_ascii_digit());
            while let Some((r, Ok(next_char))) = chars.peek() {
                if next_char.is_ascii_digit() {
                    chars.next();
                    range = range.cover(*r);
                } else {
                    break;
                }
            }
            callback(range, FormatSpecifier::Integer);
        }

        fn read_identifier<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
        where
            I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
            F: FnMut(TextRange, FormatSpecifier),
        {
            let (mut range, c) = chars.next().unwrap();
            assert!(c.as_ref().unwrap().is_alphabetic() || *c.as_ref().unwrap() == '_');
            while let Some((r, Ok(next_char))) = chars.peek() {
                if *next_char == '_' || next_char.is_ascii_digit() || next_char.is_alphabetic() {
                    chars.next();
                    range = range.cover(*r);
                } else {
                    break;
                }
            }
            callback(range, FormatSpecifier::Identifier);
        }
    }
}

impl HasFormatSpecifier for String {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();

        let mut res = Vec::with_capacity(text.len());
        unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
            res.push((
                TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
                    + offset,
                unescaped_char,
            ))
        });

        Some(res)
    }
}

impl HasFormatSpecifier for RawString {
    fn char_ranges(
        &self,
    ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
        let text = self.text().as_str();
        let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
        let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();

        let mut res = Vec::with_capacity(text.len());
        for (idx, c) in text.char_indices() {
            res.push((TextRange::at(idx.try_into().unwrap(), TextSize::of(c)) + offset, Ok(c)));
        }
        Some(res)
    }
}
<|endoftext|>


<commit_before>use actix::prelude::*;
use godcoin::prelude::*;

mod common;
pub use common::*;

#[test]
fn basic_transfer() {
    System::run(|| {
        let minter = TestMinter::new();
        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();

        let create_tx = |fee: &str, amount: Asset| {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, fee),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount,
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };

        let from_bal = minter.chain().get_balance(&from_addr, &[]).unwrap();
        let amount = get_asset("1.0000 GRAEL");
        let tx = create_tx("1.0000 GRAEL", amount);
        let fut = minter.request(MsgRequest::Broadcast(tx));
        System::current().arbiter().send(
            fut.and_then(move |res| {
                assert_eq!(res, MsgResponse::Broadcast());
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(amount));

                // The fee transfers back to the minter wallet in the form of a reward tx so it
                // must not be subtracted during the assertion
                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, from_bal.sub(amount));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}
<commit_msg>Add test for users transferring funds<commit_after>use actix::prelude::*;
use godcoin::prelude::*;

mod common;
pub use common::*;

#[test]
fn transfer_from_minter() {
    System::run(|| {
        let minter = TestMinter::new();
        let from_addr = ScriptHash::from(&minter.genesis_info().script);
        let to_addr = KeyPair::gen();

        let create_tx = |fee: &str, amount: Asset| {
            let mut tx = TransferTx {
                base: create_tx_header(TxType::TRANSFER, fee),
                from: from_addr.clone(),
                to: (&to_addr.0).into(),
                amount,
                memo: vec![],
                script: minter.genesis_info().script.clone(),
            };
            tx.append_sign(&minter.genesis_info().wallet_keys[3]);
            tx.append_sign(&minter.genesis_info().wallet_keys[0]);
            TxVariant::TransferTx(tx)
        };

        let from_bal = minter.chain().get_balance(&from_addr, &[]).unwrap();
        let amount = get_asset("1.0000 GRAEL");
        let tx = create_tx("1.0000 GRAEL", amount);
        let fut = minter.request(MsgRequest::Broadcast(tx));
        System::current().arbiter().send(
            fut.and_then(move |res| {
                assert_eq!(res, MsgResponse::Broadcast());
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let chain = minter.chain();
                let cur_bal = chain.get_balance(&to_addr.0.into(), &[]);
                assert_eq!(cur_bal, Some(amount));

                // The fee transfers back to the minter wallet in the form of a reward tx so it
                // must not be subtracted during the assertion
                let cur_bal = chain.get_balance(&from_addr, &[]);
                assert_eq!(cur_bal, from_bal.sub(amount));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}

#[test]
fn transfer_from_user() {
    System::run(|| {
        let minter = TestMinter::new();

        let user_1_addr = KeyPair::gen();
        let user_2_addr = KeyPair::gen();

        let fut = {
            let tx = {
                let mut tx = TransferTx {
                    base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                    from: ScriptHash::from(&minter.genesis_info().script),
                    to: (&user_1_addr.0).into(),
                    amount: get_asset("100.0000 GRAEL"),
                    memo: vec![],
                    script: minter.genesis_info().script.clone(),
                };
                tx.append_sign(&minter.genesis_info().wallet_keys[3]);
                tx.append_sign(&minter.genesis_info().wallet_keys[0]);
                TxVariant::TransferTx(tx)
            };
            minter.request(MsgRequest::Broadcast(tx))
        };

        System::current().arbiter().send(
            fut.and_then({
                let user_1_addr = user_1_addr.clone();
                let user_2_addr = user_2_addr.clone();
                move |res| {
                    assert_eq!(res, MsgResponse::Broadcast());
                    let tx = {
                        let mut tx = TransferTx {
                            base: create_tx_header(TxType::TRANSFER, "1.0000 GRAEL"),
                            from: (&user_1_addr.0).into(),
                            to: (&user_2_addr.0).into(),
                            amount: get_asset("99.0000 GRAEL"),
                            memo: vec![],
                            script: user_1_addr.0.clone().into(),
                        };
                        tx.append_sign(&user_1_addr);
                        TxVariant::TransferTx(tx)
                    };
                    minter
                        .request(MsgRequest::Broadcast(tx))
                        .map(|res| (res, minter))
                }
            })
            .and_then(|(res, minter)| {
                assert_eq!(res, MsgResponse::Broadcast());
                minter.produce_block().map(|_| minter)
            })
            .and_then(move |minter| {
                let user_1_bal = minter.chain().get_balance(&user_1_addr.0.into(), &[]);
                assert_eq!(user_1_bal, Some(get_asset("0.0000 GRAEL")));

                let user_2_bal = minter.chain().get_balance(&user_2_addr.0.into(), &[]);
                assert_eq!(user_2_bal, Some(get_asset("99.0000 GRAEL")));

                let minter_addr = ScriptHash::from(&minter.genesis_info().script);
                let minter_bal = minter.chain().get_balance(&minter_addr, &[]);
                // The fee loops back to the minter in a reward transaction generated by the
                // minter block production
                assert_eq!(minter_bal, Some(get_asset("901.0000 GRAEL")));

                System::current().stop();
                Ok(())
            }),
        );
    })
    .unwrap();
}
<|endoftext|>


<commit_before>//! Read configurations files.

#![deny(clippy::missing_docs_in_private_items)]

use lazy_static::lazy_static;
use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
use rustc_span::source_map;
use source_map::Span;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::{env, fmt, fs, io};

/// Gets the configuration file from arguments.
pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
        if arg.check_name(sym!(conf_file)) {
            return match arg.kind {
                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
                MetaItemKind::NameValue(ref value) => {
                    if let LitKind::Str(ref file, _) = value.kind {
                        Ok(Some(file.to_string().into()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    }
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected config format
    Toml(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(err) => err.fmt(f),
            Self::Toml(err) => err.fmt(f),
        }
    }
}

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

lazy_static! {
    static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
}

macro_rules! define_Conf {
    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
        mod helpers {
            use serde::Deserialize;
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
            pub struct Conf {
                $(
                    #[$doc]
                    #[serde(default = $config_str)]
                    #[serde(with = $config_str)]
                    pub $config: $Ty,
                )+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }

            $(
                mod $config {
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
                        use super::super::{ERRORS, Error};
                        Ok(
                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
                                ERRORS
                                    .lock()
                                    .expect("no threading here")
                                    .push(Error::Toml(e.to_string()));
                                super::$config()
                            })
                        )
                    }
                }

                #[must_use]
                fn $config() -> $Ty {
                    let x = $default;
                    x
                }
            )+
        }
    };
}

pub use self::helpers::Conf;
define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "JavaScript",
        "NaN", "NaNs",
        "OAuth",
        "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack": u64, 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold, "array_size_threshold": u64, 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools, "max_struct_bools": u64, 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
}

impl Default for Conf {
    #[must_use]
    fn default() -> Self {
        toml::from_str("").expect("we never error on empty config files")
    }
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (Conf::default(), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> (Conf, Vec<Error>) {
    let content = match fs::read_to_string(path) {
        Ok(content) => content,
        Err(err) => return default(vec![err.into()]),
    };

    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
    match toml::from_str(&content) {
        Ok(toml) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);

            let toml_ref: &Conf = &toml;

            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;

            if cyc_field.is_some() {
                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
                errors.push(Error::Toml(cyc_err));
            }

            (toml, errors)
        },
        Err(e) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
            errors.push(Error::Toml(e.to_string()));

            default(errors)
        },
    }
}
<commit_msg>Auto merge of #5611 - rrbutani:master, r=flip1995<commit_after>//! Read configurations files.

#![deny(clippy::missing_docs_in_private_items)]

use lazy_static::lazy_static;
use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
use rustc_span::source_map;
use source_map::Span;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::{env, fmt, fs, io};

/// Gets the configuration file from arguments.
pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
        if arg.check_name(sym!(conf_file)) {
            return match arg.kind {
                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
                MetaItemKind::NameValue(ref value) => {
                    if let LitKind::Str(ref file, _) = value.kind {
                        Ok(Some(file.to_string().into()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    }
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected config format
    Toml(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(err) => err.fmt(f),
            Self::Toml(err) => err.fmt(f),
        }
    }
}

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

lazy_static! {
    static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
}

macro_rules! define_Conf {
    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
        mod helpers {
            use serde::Deserialize;
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
            pub struct Conf {
                $(
                    #[$doc]
                    #[serde(default = $config_str)]
                    #[serde(with = $config_str)]
                    pub $config: $Ty,
                )+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }

            $(
                mod $config {
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
                        use super::super::{ERRORS, Error};
                        Ok(
                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
                                ERRORS
                                    .lock()
                                    .expect("no threading here")
                                    .push(Error::Toml(e.to_string()));
                                super::$config()
                            })
                        )
                    }
                }

                #[must_use]
                fn $config() -> $Ty {
                    let x = $default;
                    x
                }
            )+
        }
    };
}

pub use self::helpers::Conf;
define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
        "NaN", "NaNs",
        "OAuth",
        "OCaml",
        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TensorFlow",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
        "CamelCase",
    ].iter().map(ToString::to_string).collect()),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack": u64, 200),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
    (array_size_threshold, "array_size_threshold": u64, 512_000),
    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
    (max_struct_bools, "max_struct_bools": u64, 3),
    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
    /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
    (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
}

impl Default for Conf {
    #[must_use]
    fn default() -> Self {
        toml::from_str("").expect("we never error on empty config files")
    }
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
    // If neither of those exist, use ".".
    let mut current = env::var_os("CLIPPY_CONF_DIR")
        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
        .map_or_else(|| PathBuf::from("."), PathBuf::from);
    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
                _ => {},
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (Conf::default(), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &Path) -> (Conf, Vec<Error>) {
    let content = match fs::read_to_string(path) {
        Ok(content) => content,
        Err(err) => return default(vec![err.into()]),
    };

    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
    match toml::from_str(&content) {
        Ok(toml) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);

            let toml_ref: &Conf = &toml;

            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;

            if cyc_field.is_some() {
                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
                errors.push(Error::Toml(cyc_err));
            }

            (toml, errors)
        },
        Err(e) => {
            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
            errors.push(Error::Toml(e.to_string()));

            default(errors)
        },
    }
}
<|endoftext|>


<commit_before>use std::{fmt, fs, io};
use std::io::Read;
use syntax::{ast, codemap, ptr};
use syntax::parse::token;
use toml;

/// Get the configuration file from arguments.
pub fn file(args: &[ptr::P<ast::MetaItem>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> {
    for arg in args {
        match arg.node {
            ast::MetaItemKind::Word(ref name) |
            ast::MetaItemKind::List(ref name, _) => {
                if name == &"conf_file" {
                    return Err(("`conf_file` must be a named value", arg.span));
                }
            }
            ast::MetaItemKind::NameValue(ref name, ref value) => {
                if name == &"conf_file" {
                    return if let ast::LitKind::Str(ref file, _) = value.node {
                        Ok(Some(file.clone()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    };
                }
            }
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Toml(Vec<toml::ParserError>),
    Type(&'static str, &'static str, &'static str),
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref errs) => {
                let mut first = true;
                for err in errs {
                    if !first {
                        try!(", ".fmt(f));
                        first = false;
                    }

                    try!(err.fmt(f));
                }

                Ok(())
            }
            Error::Type(key, expected, got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            }
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

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

macro_rules! define_Conf {
    ($(#[$doc: meta] ($toml_name: tt, $rust_name: ident, $default: expr => $($ty: tt)+),)+) => {
        /// Type used to store lint configuration.
        pub struct Conf {
            $(#[$doc] pub $rust_name: define_Conf!(TY $($ty)+),)+
        }

        impl Default for Conf {
            fn default() -> Conf {
                Conf {
                    $($rust_name: define_Conf!(DEFAULT $($ty)+, $default),)+
                }
            }
        }

        impl Conf {
            /// Set the property `name` (which must be the `toml` name) to the given value
            #[allow(cast_sign_loss)]
            fn set(&mut self, name: String, value: toml::Value) -> Result<(), Error> {
                match name.as_str() {
                    $(
                        define_Conf!(PAT $toml_name) => {
                            if let Some(value) = define_Conf!(CONV $($ty)+, value) {
                                self.$rust_name = value;
                            }
                            else {
                                return Err(Error::Type(define_Conf!(EXPR $toml_name),
                                                       stringify!($($ty)+),
                                                       value.type_str()));
                            }
                        },
                    )+
                    "third-party" => {
                        // for external tools such as clippy-service
                        return Ok(());
                    }
                    _ => {
                        return Err(Error::UnknownKey(name));
                    }
                }

                Ok(())
            }
        }
    };

    // hack to convert tts
    (PAT $pat: pat) => { $pat };
    (EXPR $e: expr) => { $e };
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => { $value.as_integer().iter().filter_map(|&i| if i >= 0 { Some(i as u64) } else { None }).next() };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_slice();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            }
            else {
                Some(slice.iter().map(|v| v.as_str().unwrap_or_else(|| unreachable!()).to_owned()).collect())
            }
        }
        else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    ("blacklisted-names", blacklisted_names, ["foo", "bar", "baz"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    ("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    ("doc-valid-idents", doc_valid_idents, ["MiB", "GiB", "TiB", "PiB", "EiB", "GitHub", "NaN", "GPLv2", "GPLv3", "JavaScript", "IPv4", "IPv6", "OAuth", "TrueType"] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    ("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    ("type-complexity-threshold", type_complexity_threshold, 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    ("single-char-binding-names-threshold", max_single_char_names, 5 => u64),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    ("too-large-for-stack", too_large_for_stack, 200 => u64),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    ("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
}

/// Read the `toml` configuration file. The function will ignore “File not found” errors iif
/// `!must_exist`, in which case, it will return the default configuration.
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
    let mut conf = Conf::default();
    let mut errors = Vec::new();

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                errors.push(err.into());
                return (conf, errors);
            }

            buf
        }
        Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
            return (conf, errors);
        }
        Err(err) => {
            errors.push(err.into());
            return (conf, errors);
        }
    };

    let mut parser = toml::Parser::new(&file);
    let toml = if let Some(toml) = parser.parse() {
        toml
    } else {
        errors.push(Error::Toml(parser.errors));
        return (conf, errors);
    };

    for (key, value) in toml {
        if let Err(err) = conf.set(key, value) {
            errors.push(err);
        }
    }

    (conf, errors)
}
<commit_msg>Add "DirectX" and "OpenGL" to `doc_valid_idents`<commit_after>use std::{fmt, fs, io};
use std::io::Read;
use syntax::{ast, codemap, ptr};
use syntax::parse::token;
use toml;

/// Get the configuration file from arguments.
pub fn file(args: &[ptr::P<ast::MetaItem>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> {
    for arg in args {
        match arg.node {
            ast::MetaItemKind::Word(ref name) |
            ast::MetaItemKind::List(ref name, _) => {
                if name == &"conf_file" {
                    return Err(("`conf_file` must be a named value", arg.span));
                }
            }
            ast::MetaItemKind::NameValue(ref name, ref value) => {
                if name == &"conf_file" {
                    return if let ast::LitKind::Str(ref file, _) = value.node {
                        Ok(Some(file.clone()))
                    } else {
                        Err(("`conf_file` value must be a string", value.span))
                    };
                }
            }
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Toml(Vec<toml::ParserError>),
    Type(&'static str, &'static str, &'static str),
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref errs) => {
                let mut first = true;
                for err in errs {
                    if !first {
                        try!(", ".fmt(f));
                        first = false;
                    }

                    try!(err.fmt(f));
                }

                Ok(())
            }
            Error::Type(key, expected, got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            }
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

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

macro_rules! define_Conf {
    ($(#[$doc: meta] ($toml_name: tt, $rust_name: ident, $default: expr => $($ty: tt)+),)+) => {
        /// Type used to store lint configuration.
        pub struct Conf {
            $(#[$doc] pub $rust_name: define_Conf!(TY $($ty)+),)+
        }

        impl Default for Conf {
            fn default() -> Conf {
                Conf {
                    $($rust_name: define_Conf!(DEFAULT $($ty)+, $default),)+
                }
            }
        }

        impl Conf {
            /// Set the property `name` (which must be the `toml` name) to the given value
            #[allow(cast_sign_loss)]
            fn set(&mut self, name: String, value: toml::Value) -> Result<(), Error> {
                match name.as_str() {
                    $(
                        define_Conf!(PAT $toml_name) => {
                            if let Some(value) = define_Conf!(CONV $($ty)+, value) {
                                self.$rust_name = value;
                            }
                            else {
                                return Err(Error::Type(define_Conf!(EXPR $toml_name),
                                                       stringify!($($ty)+),
                                                       value.type_str()));
                            }
                        },
                    )+
                    "third-party" => {
                        // for external tools such as clippy-service
                        return Ok(());
                    }
                    _ => {
                        return Err(Error::UnknownKey(name));
                    }
                }

                Ok(())
            }
        }
    };

    // hack to convert tts
    (PAT $pat: pat) => { $pat };
    (EXPR $e: expr) => { $e };
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => { $value.as_integer().iter().filter_map(|&i| if i >= 0 { Some(i as u64) } else { None }).next() };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_slice();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            }
            else {
                Some(slice.iter().map(|v| v.as_str().unwrap_or_else(|| unreachable!()).to_owned()).collect())
            }
        }
        else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    ("blacklisted-names", blacklisted_names, ["foo", "bar", "baz"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    ("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    ("doc-valid-idents", doc_valid_idents, ["MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "GPLv2", "GPLv3", "GitHub", "IPv4", "IPv6", "JavaScript", "NaN", "OAuth", "OpenGL", "TrueType"] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    ("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    ("type-complexity-threshold", type_complexity_threshold, 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    ("single-char-binding-names-threshold", max_single_char_names, 5 => u64),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    ("too-large-for-stack", too_large_for_stack, 200 => u64),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    ("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
}

/// Read the `toml` configuration file. The function will ignore “File not found” errors iif
/// `!must_exist`, in which case, it will return the default configuration.
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
    let mut conf = Conf::default();
    let mut errors = Vec::new();

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                errors.push(err.into());
                return (conf, errors);
            }

            buf
        }
        Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
            return (conf, errors);
        }
        Err(err) => {
            errors.push(err.into());
            return (conf, errors);
        }
    };

    let mut parser = toml::Parser::new(&file);
    let toml = if let Some(toml) = parser.parse() {
        toml
    } else {
        errors.push(Error::Toml(parser.errors));
        return (conf, errors);
    };

    for (key, value) in toml {
        if let Err(err) = conf.set(key, value) {
            errors.push(err);
        }
    }

    (conf, errors)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use resource_task::{LoadResponse, Metadata, Done, LoadData, start_sending};
use file_loader;

use std::os;
use url::Url;
use StatusOk = http::status::Ok;

pub fn factory(mut load_data: LoadData, start_chan: Sender<LoadResponse>) {
    match load_data.url.non_relative_scheme_data().unwrap() {
        "blank" => {
            let chan = start_sending(start_chan, Metadata {
                final_url: load_data.url,
                content_type: Some(("text".to_string(), "html".to_string())),
                charset: Some("utf-8".to_string()),
                headers: None,
                status: StatusOk,
            });
            chan.send(Done(Ok(())));
            return
        }
        "crash" => fail!("Loading the about:crash URL."),
        "failure" => {
            // FIXME: Find a way to load this without relying on the `../src` directory.
            let mut path = os::self_exe_path().expect("can't get exe path");
            path.pop();
            path.push_many(["src", "test", "html", "failure.html"]);
            load_data.url = Url::from_file_path(&path).unwrap();
        }
        _ => {
            start_sending(start_chan, Metadata::default(load_data.url))
                .send(Done(Err("Unknown about: URL.".to_string())));
            return
        }
    };
    file_loader::factory(load_data, start_chan)
}
<commit_msg>Fix incorrect path to about:failure source.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use resource_task::{LoadResponse, Metadata, Done, LoadData, start_sending};
use file_loader;

use std::os;
use url::Url;
use StatusOk = http::status::Ok;

pub fn factory(mut load_data: LoadData, start_chan: Sender<LoadResponse>) {
    match load_data.url.non_relative_scheme_data().unwrap() {
        "blank" => {
            let chan = start_sending(start_chan, Metadata {
                final_url: load_data.url,
                content_type: Some(("text".to_string(), "html".to_string())),
                charset: Some("utf-8".to_string()),
                headers: None,
                status: StatusOk,
            });
            chan.send(Done(Ok(())));
            return
        }
        "crash" => fail!("Loading the about:crash URL."),
        "failure" => {
            // FIXME: Find a way to load this without relying on the `../src` directory.
            let mut path = os::self_exe_path().expect("can't get exe path");
            path.pop();
            if !path.join(Path::new("./tests/")).is_dir() {
                path.pop();
            }
            path.push_many(["tests", "html", "failure.html"]);
            assert!(path.exists());
            load_data.url = Url::from_file_path(&path).unwrap();
        }
        _ => {
            start_sending(start_chan, Metadata::default(load_data.url))
                .send(Done(Err("Unknown about: URL.".to_string())));
            return
        }
    };
    file_loader::factory(load_data, start_chan)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Microdata {}

impl Microdata {
    //[Pref="dom.microdata.testing.enabled"]
    pub fn parse(doc: &Document) -> String {

        let ele = doc.upcast::<Node>();
        let mut start_vcard = false;
        let mut result : String = String::new();
        let mut master_map : HashMap<String, HashMap<String, String>> = HashMap::new();
        let mut master_key : String = String::new();

        result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
        result += doc.url().as_str();
        result += "\nNAME:";
        result += doc.Title().trim();
        result += "\n";

        for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>){
            if element.is::<HTMLElement>() {
                if element.has_attribute(&local_name!("itemtype")){
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                    if !atoms.is_empty() {
                        let val = atoms.remove(0);
                        if val.trim() == "http://microformats.org/profile/hcard"{
                            if !start_vcard {
                                start_vcard = true;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if start_vcard {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                    if !atoms.is_empty() {
                        let temp_key = atoms.remove(0);
                        if element.has_attribute(&local_name!("itemscope")){
                            master_key = String::from(temp_key.trim()).to_owned();
                            let dup_master_key = Cow::Borrowed(&master_key);
                            master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        } else {
                            let temp = String::from(temp_key.trim()).to_owned();
                            let dup_key = Cow::Borrowed(&temp);
                            let data = String::from(element.GetInnerHTML().unwrap());
                            let dup_master_key = Cow::Borrowed(&master_key);
                            let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                            temp_map.insert(dup_key.to_string(), String::from(data));
                        }
                    }
                }
            }
        }
        //println!("{:?}", master_map);
        for (info_type, detail_map) in &master_map {
            //println!("{} -> {:?}", info_type, detail_map);
            match info_type.as_str() {
                "n" => {
                    let mut given_name = "";
                    let mut family_name = "";
                    if detail_map.contains_key("family-name") {
                        family_name = detail_map.get("family-name").unwrap();
                    }
                    if detail_map.contains_key("given-name") {
                        given_name = detail_map.get("given-name").unwrap();
                    }
                    result += format!("N:{};{}\n", family_name, given_name).as_str();
                    result += format!("FN:{} {}\n", given_name, family_name).as_str();
                },
                "org" => {
                    let mut organization_unit = "";
                    let mut organization_name = "";
                    if detail_map.contains_key("organization-unit"){
                        organization_unit = detail_map.get("organization-unit").unwrap();
                    }
                    if detail_map.contains_key("organization-name"){
                        organization_name = detail_map.get("organization-name").unwrap();
                    }
                    result += format!("ORG:{};{}\n", organization_name, organization_unit).as_str();
                },
                "tel" => {

                },
                "adr" => {

                },
                _ => {},
            }
        }
        result += "END:VCARD";
        println!("{}", result);
        return result;
    }
}
<commit_msg>Adding adr to vCard<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Microdata {}

impl Microdata {
    //[Pref="dom.microdata.testing.enabled"]
    pub fn parse(doc: &Document) -> String {

        let ele = doc.upcast::<Node>();
        let mut start_vcard = false;
        let mut result : String = String::new();
        let mut master_map : HashMap<String, HashMap<String, String>> = HashMap::new();
        let mut master_key : String = String::new();

        result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
        result += doc.url().as_str();

        let title = doc.Title();
        if !title.is_empty() && !title.trim().is_empty() {
            result += "\nNAME:";
            result += title.trim();
        }

        result += "\n";
        let mut sex : String = String::new();
        let mut gender_identity : String = String::new();

        for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>){
            if element.is::<HTMLElement>() {
                if element.has_attribute(&local_name!("itemtype")){
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                    if !atoms.is_empty() {
                        let val = atoms.remove(0);
                        if val.trim() == "http://microformats.org/profile/hcard"{
                            if !start_vcard {
                                start_vcard = true;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if start_vcard {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                    if !atoms.is_empty() {
                        let temp_key = atoms.remove(0);
                        if element.has_attribute(&local_name!("itemscope")){
                            master_key = String::from(temp_key.trim()).to_owned();
                            let dup_master_key = Cow::Borrowed(&master_key);
                            master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        } else {
                            let temp = String::from(temp_key.trim()).to_owned();
                            let dup_key = Cow::Borrowed(&temp);
                            let data = String::from(element.GetInnerHTML().unwrap());
                            let dup_master_key = Cow::Borrowed(&master_key);
                            let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                            temp_map.insert(dup_key.to_string(), String::from(data));
                        }
                    }
                }
            }
        }
        //println!("{:?}", master_map);
        for (info_type, detail_map) in &master_map {
            //println!("{} -> {:?}", info_type, detail_map);
            match info_type.as_str() {
                "n" => {

                    let mut n_value : String = String::new();

                    let name_parts = ["family-name", "given-name", "additional-name", "honorific-prefix", "honorific-suffix"];
                    for part in name_parts.iter() {
                        if detail_map.contains_key(*part) {
                            n_value += format!("{};",detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    n_value.pop();

                    result += format!("{}:{}\n", info_type.as_str(), n_value).as_str();

                },
                "org" => {

                    let mut org_value : String = String::new();

                    let org_parts = ["organization-name", "organization-unit"];
                    for part in org_parts.iter() {
                        if detail_map.contains_key(*part) {
                            org_value += format!("{};",detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    org_value.pop();

                    result += format!("{}:{}\n", info_type.as_str(), org_value).as_str();

                },
                "tel" => {

                },
                "adr" => {

                    let mut adr_value : String = String::new();

                    let adr_parts = ["street-address", "locality", "region", "postal-code", "country-name", "post-office-box", "extended-address"];
                    for part in adr_parts.iter() {
                        if detail_map.contains_key(*part) {
                            adr_value += format!("{};",detail_map.get(*part).unwrap()).as_str();
                        }
                    }
                    adr_value.pop();

                    result += format!("{}:{}\n", info_type.as_str(), adr_value).as_str();

                },
                _ => {},
            }
        }

        result += "END:VCARD";
        println!("{}", result);
        return result;
    }
}
<|endoftext|>