<commit_before>// Implements http://rosettacode.org/wiki/S-Expressions
//
// This implementation isn't based on anything in particular, although it's probably informed by a
// lot of Rust's JSON encoding code.  It should be very fast (both encoding and decoding the toy
// example here takes under a microsecond on my machine) and tries to avoid unnecessary allocation.
//
// In a real implementation, most of this would be private, with only a few visible functions, and
// there would be somewhat nicer signatures (in particular, the fact that ParseContext has to be
// mutable would get annoying in real code pretty quickly, so it would probably be split out).
//
// It supports the ability to read individual atoms, not just lists, although whether this is
// useful is questionable.
//
// Caveats: Does not support symbols vs. non-symbols (it wouldn't be hard, but it would greatly
// complicate setting up our test structure since we'd have to force it to go through functions
// that checked to make sure Symbols couldn't have spaces, or slow down our parser by checking for
// this information each time, which is obnoxious).  Does not support string escaping, because the
// decoding technique doesn't allocate extra space for strings.  Does support numbers, but only
// float types (supporting more types is possible but would complicate the code significantly).
//
#![feature(slicing_syntax, globs, if_let)]

extern crate arena;
extern crate test;

use arena::TypedArena;

use std::io;
use std::num::{mod, Float};
use self::SExp::*;
use self::Error::*;
use self::Token::*;

#[deriving(PartialEq,Show)]
// The actual SExp structure.  Supports f64s, lists, and string literals.  Note that it takes
// everything by reference, rather than owning it--this is mostly done just so we can allocate
// SExps statically (since we don't have to call Vec).  It does complicate the code a bit,
// requiring us to have a ParseContext that holds an arena where lists are actually allocated.
enum SExp<'a> {
    F64(f64), // Float literal: 0.5
    List(&'a [SExp<'a>]), // List of SExps: ( a b c)
    Str(&'a str), // Plain old string literal: "abc"
}

// Errors that can be thrown by the parser.
#[deriving(PartialEq,Show)]
enum Error {
    NoReprForFloat, // If the float is NaN, Infinity, etc.
    UnterminatedStringLiteral, // Missing an end double quote during string parsing
    IoError(io::IoError), // Some other kind of I/O error
    IncorrectCloseDelimiter, // ) appeared where it shouldn't (usually as the first token)
    UnexpectedEOF, // Usually means a missing ), but could also mean there were no tokens at all.
    ExpectedEOF, // More tokens after the list is finished, or after a literal if there is no list.
}

// Tokens returned from the token stream.
#[deriving(PartialEq)]
enum Token<'a> {
    ListStart, // Left parenthesis
    ListEnd, // Right parenthesis
    Literal(SExp<'a>), // String or float literal, quotes removed.
    EOF, // Stream is out of tokens.
}

// An iterator over a string that yields a stream of Tokens.
// Implementation note: it probably seems weird to store first, rest, AND string, since they should
// all be derivable from string.  But see below.
struct Tokens<'a> {
    string: &'a str, // The part of the string that still needs to be parsed
    first: Option<char>, // The first character to parse
    rest: &'a str, // The rest of the string after the first character
}

impl<'a> Tokens<'a> {
    // Initialize a token stream for a given string.
    fn new(string: &str) -> Tokens {
        match string.slice_shift_char() {
            Some((ch, s)) =>  Tokens { string: string, first: Some(ch), rest: s },
            None => Tokens { string: string, first: None, rest: string }
        }
    }

    // Utility function to update information in the iterator.  It might not be performant to keep
    // rest cached, but there are times where we don't know exactly what string is (at least, not
    // in a way that we can *safely* reconstruct it without allocating), so we keep both here.
    // With some unsafe code we could probably get rid of one of them (and maybe first, too).
    fn update(&mut self, string: &'a str) {
        self.string = string;
        if let Some((ch, s)) = string.slice_shift_char() {
            self.first = Some(ch);
            self.rest = s;
        } else {
            self.first = None;
        };
    }

    // This is where the lexing happens.  Note that it does not handle string escaping.
    fn next(&mut self) -> Result<Token<'a>, Error> {
        loop {
            match self.first {
                // List start
                Some('(') => {
                    self.update(self.rest);
                    return Ok(ListStart)
                },
                // List end
                Some(')') => {
                    self.update(self.rest);
                    return Ok(ListEnd)
                },
                // Quoted literal start
                Some('"') => {
                    // Split the string at most once.  This lets us get a
                    // reference to the next piece of the string without having
                    // to loop through the string again.
                    let mut iter = self.rest.splitn(1, '"');
                    // The first time splitn is run it will never return None, so this is safe.
                    let str = iter.next().unwrap();
                    match iter.next() {
                        // Extract the interior of the string without allocating.  If we want to
                        // handle string escaping, we would have to allocate at some point though.
                        Some(s) => {
                            self.update(s);
                            return Ok(Literal(Str(str)))
                        },
                        None => return Err(UnterminatedStringLiteral)
                    }
                },
                // Plain old literal start
                Some(c) => {
                    // Skip whitespace.  This could probably be made more efficient.
                    if c.is_whitespace() {
                        self.update(self.rest);
                        continue
                    }
                    // Since we've exhausted all other possibilities, this must be a real literal.
                    // Unlike the quoted case, it's not an error to encounter EOF before whitespace.
                    let mut end_ch = None;
                    let str = {
                        let mut iter = self.string.splitn(1, |ch: char| {
                            let term = ch == ')' || ch == '(';
                            if term { end_ch = Some(ch) }
                            term || ch.is_whitespace()
                        });
                        // The first time splitn is run it will never return None, so this is safe.
                        let str = iter.next().unwrap();
                        self.rest = iter.next().unwrap_or("");
                        str
                    };
                    match end_ch {
                        // self.string will be incorrect in the Some(_) case.  The only reason it's
                        // okay is because the next time next() is called in this case, we know it
                        // will be '(' or ')', so it will never reach any code that actually looks
                        // at self.string.  In a real implementation this would be enforced by
                        // visibility rules.
                        Some(_) => self.first = end_ch,
                        None => self.update(self.rest)
                    }
                    return Ok(Literal(parse_literal(str)));
                }
                None => return Ok(EOF)
            }
        }
    }
}

// Convenience method to turn I/O errors into SExp Errors, inspired by the JSON encoder.
fn from_io_result<T>(res: io::IoResult<T>) -> Result<T, Error> {
    res.map_err( |err| IoError(err) )
}

// This is not the most efficient way to do this, because we end up going over numeric literals
// twice, but it avoids having to write our own number parsing logic.
fn parse_literal(literal: &str) -> SExp {
    match literal.bytes().next() {
        Some(b'0'...b'9') | Some(b'-') => match num::from_str_radix(literal, 10) {
            Some(f) => F64(f),
            None => Str(literal)
        },
        _ => Str(literal)
    }
}

// Parse context, holds information required by the parser (and owns any allocations it makes)
struct ParseContext<'a> {
    string: &'a str, // The string being parsed.  Not required, but convenient.
    arena: Option<TypedArena<Vec<SExp<'a>>>>, // Arena holding any allocations made by the parser.
    stack: Vec<Vec<SExp<'a>>>, // Stored in the parse context so it can be reused once allocated.
}

impl<'a> ParseContext<'a> {
    // Create a new parse context from a given string
    fn new(string: &'a str) -> ParseContext<'a> {
        ParseContext { string: string, arena: None, stack: Vec::new() }
    }
}

impl<'a> SExp<'a> {
    // Serialize a SExp.
    fn encode<T: io::Writer>(&self, writer: &mut T) -> Result<(), Error> {
        match *self {
            F64(f) => match f.classify() {
                // We don't want to identify NaN, Infinity, etc. as floats.
                num::FPNormal | num::FPZero => from_io_result(write!(writer, "{}", f)),
                _ => Err(NoReprForFloat)
            },
            List(ref l) => {
                // Writing a list is very straightforward--write a left parenthesis, then
                // recursively call encode on each member, and then write a right parenthesis.  The
                // only reason the logic is as long as it is is to make sure we don't write
                // unnecessary spaces between parentheses in the zero or one element cases.
                try!(from_io_result(writer.write_char('(')));
                let mut iter = l.iter();
                match iter.next() {
                    Some(sexp) => {
                        try!(sexp.encode(writer));
                        for sexp in iter {
                            try!(from_io_result(writer.write_char(' ')));
                            try!(sexp.encode(writer));
                        }
                    },
                    None => (),
                }
                from_io_result(writer.write_char(')'))
            },
            Str(s) => from_io_result(write!(writer, "\"{}\"", s)),
        }
    }

    // Deserialize a SExp.
    fn parse(ctx: &'a mut ParseContext<'a>) -> Result<SExp<'a>, Error> {
        ctx.arena = Some(TypedArena::new());
        // Hopefully this unreachable! gets optimized out, because it should literally be
        // unreachable.
        let arena = match ctx.arena {
            Some(ref mut arena) => arena,
            None => unreachable!()
        };
        let ParseContext {string, ref mut stack, .. } = *ctx;
        // Make sure the stack is cleared--we keep it in the context to avoid unnecessary
        // reallocation between parses (if you need to remember old parse information for a new
        // list, you can pass in a new context).
        stack.clear();
        let mut tokens = Tokens::new(string);
        // First, we check the very first token to see if we're parsing a full list.  It
        // simplifies parsing a lot in the subsequent code if we can assume that.
        let next = tokens.next();
        let mut list = match try!(next) {
            ListStart => Vec::new(),
            Literal(s) => return if try!(tokens.next()) == EOF { Ok(s) } else { Err(ExpectedEOF) },
            ListEnd => return Err(IncorrectCloseDelimiter),
            EOF => return Err(UnexpectedEOF),
        };

        // We know we're in a list if we got this far.
        loop {
            let tok = tokens.next();
            match try!(tok) {
                ListStart => {
                    // We push the previous context onto our stack when we start reading a new list.
                    stack.push(list);
                    list = Vec::new()
                },
                Literal(s) => list.push(s), // Plain old literal, push it onto the current list
                ListEnd => match stack.pop() { // Pop the old context off the stack on list end.
                    Some(mut l) => {
                        // We allocate a slot for the current list in our parse context (needed for
                        // safety) before pushing it onto its parent list.
                        l.push(List(arena.alloc(list).as_slice()));
                        // Now reset the current list to the parent list
                        list = l;
                    },
                    // There was nothing on the stack, so we're at the end of the topmost list.
                    // The check to make sure there are no more tokens is required for correctness.
                    None => return match try!(tokens.next()) {
                        EOF => Ok(List(arena.alloc(list).as_slice())),
                        _ => Err(ExpectedEOF),
                    }
                },
                // We encountered an EOF before the list ended--that's an error.
                EOF => return Err(UnexpectedEOF),
            }
        }
    }

    // Convenience method for the common case where you just want to encode a SExp as a String.
    fn buffer_encode(&self) -> Result<String, Error> {
        let mut m = io::MemWriter::new();
        try!(self.encode(&mut m));
        // Because encode() only ever writes valid UTF-8, we can safely skip the secondary check we
        // normally have to do when converting from Vec<u8> to String.  If we didn't know that the
        // buffer was already UTF-8, we'd want to call container_as_str() here.
        unsafe { Ok(String::from_utf8_unchecked(m.into_inner())) }
    }
}

const SEXP_STRUCT: SExp<'static> = List(&[
    List(&[Str("data"), Str("quoted data"), F64(123.), F64(4.5)]),
    List(&[Str("data"), List(&[Str("!@#"), List(&[F64(4.5)]), Str("(more"), Str("data)")])]),
]);

fn try_encode() -> Result<String, Error> {
    SEXP_STRUCT.buffer_encode()
}

const SEXP_STRING_IN: &'static str = r#"((data "quoted data" 123 4.5)
(data (!@# (4.5) "(more" "data)")))"#;

fn try_decode<'a>(ctx: &'a mut ParseContext<'a>) -> Result<SExp<'a>, Error> {
    SExp::parse(ctx)
}

#[cfg(not(test))]
fn main() {
    println!("{}", try_encode());
    let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
    println!("{}", try_decode(ctx));
}

#[bench]
fn bench_decode(b: &mut test::Bencher)
{
    b.iter(|| {
        let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
        assert!(try_decode(ctx).is_ok());
    })
}

#[bench]
fn bench_encode(b: &mut test::Bencher)
{
    b.iter(|| {
        assert!(try_encode().is_ok());
    })
}

#[test]
fn test_sexp_encode() {
    const SEXP_STRING: &'static str =
r#"(("data" "quoted data" 123 4.5) ("data" ("!@#" (4.5) "(more" "data)")))"#;
    assert_eq!(Ok(SEXP_STRING), try_encode().as_ref().map( |s| s[]));
}

#[test]
fn test_sexp_decode() {
    let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
    assert_eq!(Ok(SEXP_STRUCT), try_decode(ctx));
}
<commit_msg>un-feature-gated if let<commit_after>// Implements http://rosettacode.org/wiki/S-Expressions
//
// This implementation isn't based on anything in particular, although it's probably informed by a
// lot of Rust's JSON encoding code.  It should be very fast (both encoding and decoding the toy
// example here takes under a microsecond on my machine) and tries to avoid unnecessary allocation.
//
// In a real implementation, most of this would be private, with only a few visible functions, and
// there would be somewhat nicer signatures (in particular, the fact that ParseContext has to be
// mutable would get annoying in real code pretty quickly, so it would probably be split out).
//
// It supports the ability to read individual atoms, not just lists, although whether this is
// useful is questionable.
//
// Caveats: Does not support symbols vs. non-symbols (it wouldn't be hard, but it would greatly
// complicate setting up our test structure since we'd have to force it to go through functions
// that checked to make sure Symbols couldn't have spaces, or slow down our parser by checking for
// this information each time, which is obnoxious).  Does not support string escaping, because the
// decoding technique doesn't allocate extra space for strings.  Does support numbers, but only
// float types (supporting more types is possible but would complicate the code significantly).
//
#![feature(slicing_syntax, globs)]

extern crate arena;
extern crate test;

use arena::TypedArena;

use std::io;
use std::num::{mod, Float};
use self::SExp::*;
use self::Error::*;
use self::Token::*;

#[deriving(PartialEq,Show)]
// The actual SExp structure.  Supports f64s, lists, and string literals.  Note that it takes
// everything by reference, rather than owning it--this is mostly done just so we can allocate
// SExps statically (since we don't have to call Vec).  It does complicate the code a bit,
// requiring us to have a ParseContext that holds an arena where lists are actually allocated.
enum SExp<'a> {
    F64(f64), // Float literal: 0.5
    List(&'a [SExp<'a>]), // List of SExps: ( a b c)
    Str(&'a str), // Plain old string literal: "abc"
}

// Errors that can be thrown by the parser.
#[deriving(PartialEq,Show)]
enum Error {
    NoReprForFloat, // If the float is NaN, Infinity, etc.
    UnterminatedStringLiteral, // Missing an end double quote during string parsing
    IoError(io::IoError), // Some other kind of I/O error
    IncorrectCloseDelimiter, // ) appeared where it shouldn't (usually as the first token)
    UnexpectedEOF, // Usually means a missing ), but could also mean there were no tokens at all.
    ExpectedEOF, // More tokens after the list is finished, or after a literal if there is no list.
}

// Tokens returned from the token stream.
#[deriving(PartialEq)]
enum Token<'a> {
    ListStart, // Left parenthesis
    ListEnd, // Right parenthesis
    Literal(SExp<'a>), // String or float literal, quotes removed.
    EOF, // Stream is out of tokens.
}

// An iterator over a string that yields a stream of Tokens.
// Implementation note: it probably seems weird to store first, rest, AND string, since they should
// all be derivable from string.  But see below.
struct Tokens<'a> {
    string: &'a str, // The part of the string that still needs to be parsed
    first: Option<char>, // The first character to parse
    rest: &'a str, // The rest of the string after the first character
}

impl<'a> Tokens<'a> {
    // Initialize a token stream for a given string.
    fn new(string: &str) -> Tokens {
        match string.slice_shift_char() {
            Some((ch, s)) =>  Tokens { string: string, first: Some(ch), rest: s },
            None => Tokens { string: string, first: None, rest: string }
        }
    }

    // Utility function to update information in the iterator.  It might not be performant to keep
    // rest cached, but there are times where we don't know exactly what string is (at least, not
    // in a way that we can *safely* reconstruct it without allocating), so we keep both here.
    // With some unsafe code we could probably get rid of one of them (and maybe first, too).
    fn update(&mut self, string: &'a str) {
        self.string = string;
        if let Some((ch, s)) = string.slice_shift_char() {
            self.first = Some(ch);
            self.rest = s;
        } else {
            self.first = None;
        };
    }

    // This is where the lexing happens.  Note that it does not handle string escaping.
    fn next(&mut self) -> Result<Token<'a>, Error> {
        loop {
            match self.first {
                // List start
                Some('(') => {
                    self.update(self.rest);
                    return Ok(ListStart)
                },
                // List end
                Some(')') => {
                    self.update(self.rest);
                    return Ok(ListEnd)
                },
                // Quoted literal start
                Some('"') => {
                    // Split the string at most once.  This lets us get a
                    // reference to the next piece of the string without having
                    // to loop through the string again.
                    let mut iter = self.rest.splitn(1, '"');
                    // The first time splitn is run it will never return None, so this is safe.
                    let str = iter.next().unwrap();
                    match iter.next() {
                        // Extract the interior of the string without allocating.  If we want to
                        // handle string escaping, we would have to allocate at some point though.
                        Some(s) => {
                            self.update(s);
                            return Ok(Literal(Str(str)))
                        },
                        None => return Err(UnterminatedStringLiteral)
                    }
                },
                // Plain old literal start
                Some(c) => {
                    // Skip whitespace.  This could probably be made more efficient.
                    if c.is_whitespace() {
                        self.update(self.rest);
                        continue
                    }
                    // Since we've exhausted all other possibilities, this must be a real literal.
                    // Unlike the quoted case, it's not an error to encounter EOF before whitespace.
                    let mut end_ch = None;
                    let str = {
                        let mut iter = self.string.splitn(1, |ch: char| {
                            let term = ch == ')' || ch == '(';
                            if term { end_ch = Some(ch) }
                            term || ch.is_whitespace()
                        });
                        // The first time splitn is run it will never return None, so this is safe.
                        let str = iter.next().unwrap();
                        self.rest = iter.next().unwrap_or("");
                        str
                    };
                    match end_ch {
                        // self.string will be incorrect in the Some(_) case.  The only reason it's
                        // okay is because the next time next() is called in this case, we know it
                        // will be '(' or ')', so it will never reach any code that actually looks
                        // at self.string.  In a real implementation this would be enforced by
                        // visibility rules.
                        Some(_) => self.first = end_ch,
                        None => self.update(self.rest)
                    }
                    return Ok(Literal(parse_literal(str)));
                }
                None => return Ok(EOF)
            }
        }
    }
}

// Convenience method to turn I/O errors into SExp Errors, inspired by the JSON encoder.
fn from_io_result<T>(res: io::IoResult<T>) -> Result<T, Error> {
    res.map_err( |err| IoError(err) )
}

// This is not the most efficient way to do this, because we end up going over numeric literals
// twice, but it avoids having to write our own number parsing logic.
fn parse_literal(literal: &str) -> SExp {
    match literal.bytes().next() {
        Some(b'0'...b'9') | Some(b'-') => match num::from_str_radix(literal, 10) {
            Some(f) => F64(f),
            None => Str(literal)
        },
        _ => Str(literal)
    }
}

// Parse context, holds information required by the parser (and owns any allocations it makes)
struct ParseContext<'a> {
    string: &'a str, // The string being parsed.  Not required, but convenient.
    arena: Option<TypedArena<Vec<SExp<'a>>>>, // Arena holding any allocations made by the parser.
    stack: Vec<Vec<SExp<'a>>>, // Stored in the parse context so it can be reused once allocated.
}

impl<'a> ParseContext<'a> {
    // Create a new parse context from a given string
    fn new(string: &'a str) -> ParseContext<'a> {
        ParseContext { string: string, arena: None, stack: Vec::new() }
    }
}

impl<'a> SExp<'a> {
    // Serialize a SExp.
    fn encode<T: io::Writer>(&self, writer: &mut T) -> Result<(), Error> {
        match *self {
            F64(f) => match f.classify() {
                // We don't want to identify NaN, Infinity, etc. as floats.
                num::FPNormal | num::FPZero => from_io_result(write!(writer, "{}", f)),
                _ => Err(NoReprForFloat)
            },
            List(ref l) => {
                // Writing a list is very straightforward--write a left parenthesis, then
                // recursively call encode on each member, and then write a right parenthesis.  The
                // only reason the logic is as long as it is is to make sure we don't write
                // unnecessary spaces between parentheses in the zero or one element cases.
                try!(from_io_result(writer.write_char('(')));
                let mut iter = l.iter();
                match iter.next() {
                    Some(sexp) => {
                        try!(sexp.encode(writer));
                        for sexp in iter {
                            try!(from_io_result(writer.write_char(' ')));
                            try!(sexp.encode(writer));
                        }
                    },
                    None => (),
                }
                from_io_result(writer.write_char(')'))
            },
            Str(s) => from_io_result(write!(writer, "\"{}\"", s)),
        }
    }

    // Deserialize a SExp.
    fn parse(ctx: &'a mut ParseContext<'a>) -> Result<SExp<'a>, Error> {
        ctx.arena = Some(TypedArena::new());
        // Hopefully this unreachable! gets optimized out, because it should literally be
        // unreachable.
        let arena = match ctx.arena {
            Some(ref mut arena) => arena,
            None => unreachable!()
        };
        let ParseContext {string, ref mut stack, .. } = *ctx;
        // Make sure the stack is cleared--we keep it in the context to avoid unnecessary
        // reallocation between parses (if you need to remember old parse information for a new
        // list, you can pass in a new context).
        stack.clear();
        let mut tokens = Tokens::new(string);
        // First, we check the very first token to see if we're parsing a full list.  It
        // simplifies parsing a lot in the subsequent code if we can assume that.
        let next = tokens.next();
        let mut list = match try!(next) {
            ListStart => Vec::new(),
            Literal(s) => return if try!(tokens.next()) == EOF { Ok(s) } else { Err(ExpectedEOF) },
            ListEnd => return Err(IncorrectCloseDelimiter),
            EOF => return Err(UnexpectedEOF),
        };

        // We know we're in a list if we got this far.
        loop {
            let tok = tokens.next();
            match try!(tok) {
                ListStart => {
                    // We push the previous context onto our stack when we start reading a new list.
                    stack.push(list);
                    list = Vec::new()
                },
                Literal(s) => list.push(s), // Plain old literal, push it onto the current list
                ListEnd => match stack.pop() { // Pop the old context off the stack on list end.
                    Some(mut l) => {
                        // We allocate a slot for the current list in our parse context (needed for
                        // safety) before pushing it onto its parent list.
                        l.push(List(arena.alloc(list).as_slice()));
                        // Now reset the current list to the parent list
                        list = l;
                    },
                    // There was nothing on the stack, so we're at the end of the topmost list.
                    // The check to make sure there are no more tokens is required for correctness.
                    None => return match try!(tokens.next()) {
                        EOF => Ok(List(arena.alloc(list).as_slice())),
                        _ => Err(ExpectedEOF),
                    }
                },
                // We encountered an EOF before the list ended--that's an error.
                EOF => return Err(UnexpectedEOF),
            }
        }
    }

    // Convenience method for the common case where you just want to encode a SExp as a String.
    fn buffer_encode(&self) -> Result<String, Error> {
        let mut m = io::MemWriter::new();
        try!(self.encode(&mut m));
        // Because encode() only ever writes valid UTF-8, we can safely skip the secondary check we
        // normally have to do when converting from Vec<u8> to String.  If we didn't know that the
        // buffer was already UTF-8, we'd want to call container_as_str() here.
        unsafe { Ok(String::from_utf8_unchecked(m.into_inner())) }
    }
}

const SEXP_STRUCT: SExp<'static> = List(&[
    List(&[Str("data"), Str("quoted data"), F64(123.), F64(4.5)]),
    List(&[Str("data"), List(&[Str("!@#"), List(&[F64(4.5)]), Str("(more"), Str("data)")])]),
]);

fn try_encode() -> Result<String, Error> {
    SEXP_STRUCT.buffer_encode()
}

const SEXP_STRING_IN: &'static str = r#"((data "quoted data" 123 4.5)
(data (!@# (4.5) "(more" "data)")))"#;

fn try_decode<'a>(ctx: &'a mut ParseContext<'a>) -> Result<SExp<'a>, Error> {
    SExp::parse(ctx)
}

#[cfg(not(test))]
fn main() {
    println!("{}", try_encode());
    let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
    println!("{}", try_decode(ctx));
}

#[bench]
fn bench_decode(b: &mut test::Bencher)
{
    b.iter(|| {
        let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
        assert!(try_decode(ctx).is_ok());
    })
}

#[bench]
fn bench_encode(b: &mut test::Bencher)
{
    b.iter(|| {
        assert!(try_encode().is_ok());
    })
}

#[test]
fn test_sexp_encode() {
    const SEXP_STRING: &'static str =
r#"(("data" "quoted data" 123 4.5) ("data" ("!@#" (4.5) "(more" "data)")))"#;
    assert_eq!(Ok(SEXP_STRING), try_encode().as_ref().map( |s| s[]));
}

#[test]
fn test_sexp_decode() {
    let ref mut ctx = ParseContext::new(SEXP_STRING_IN);
    assert_eq!(Ok(SEXP_STRUCT), try_decode(ctx));
}
<|endoftext|>


<commit_before>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// https://github.com/BurntSushi/ripgrep
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<commit_msg>ignore/types: improve bazel globs<commit_after>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// https://github.com/BurntSushi/ripgrep
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.cs", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &["Gemfile", "*.gemspec", ".irbrc", "Rakefile", "*.rb"]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<|endoftext|>


<commit_before>use std::sync::Arc;

use ra_db::{CrateGraph, SourceRootId, SourceDatabase};
use relative_path::RelativePath;
use test_utils::{assert_eq_text, covers};

use crate::{
    ItemMap,
    PersistentHirDatabase,
    mock::MockDatabase,
    module_tree::ModuleId,
};
use super::Resolution;

fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (db, pos) = MockDatabase::with_position(fixture);
    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

/// Sets the crate root to the file of the cursor marker
fn item_map_custom_crate_root(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (mut db, pos) = MockDatabase::with_position(fixture);

    let mut crate_graph = CrateGraph::default();
    crate_graph.add_crate_root(pos.file_id);
    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
    let mut lines = map[module_id]
        .items
        .iter()
        .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
        .collect::<Vec<_>>();
    lines.sort();
    let actual = lines.join("\n");
    let expected = expected
        .trim()
        .lines()
        .map(|it| it.trim())
        .collect::<Vec<_>>()
        .join("\n");
    assert_eq_text!(&expected, &actual);

    fn dump_resolution(resolution: &Resolution) -> &'static str {
        match (
            resolution.def.types.is_some(),
            resolution.def.values.is_some(),
        ) {
            (true, true) => "t v",
            (true, false) => "t",
            (false, true) => "v",
            (false, false) => "_",
        }
    }
}

#[test]
fn item_map_smoke_test() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn use_as() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::Baz as Foo;
        <|>

        //- /foo/mod.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Foo: t v
            foo: t
        ",
    );
}

#[test]
fn use_trees() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::{Baz, Quux};
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        pub enum Quux {};
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            Quux: t
            foo: t
        ",
    );
}

#[test]
fn re_exports() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn module_resolution_works_for_non_standard_filenames() {
    let (item_map, module_id) = item_map_custom_crate_root(
        "
        //- /my_library.rs
        mod foo;
        use self::foo::Bar;
        <|>
        //- /foo/mod.rs
        pub struct Bar;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Bar: t v
            foo: t
        ",
    );
}

#[test]
fn name_res_works_for_broken_modules() {
    covers!(name_res_works_for_broken_modules);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo // no `;`, no body

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: _
        ",
    );
}

#[test]
fn item_map_contains_items_from_expansions() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        salsa::query_group! {
            trait Baz {}
        }
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t
            foo: t
        ",
    );
}

#[test]
fn item_map_using_self() {
    let (item_map, module_id) = item_map(
        "
            //- /lib.rs
            mod foo;
            use crate::foo::bar::Baz::{self};
            <|>
            //- /foo/mod.rs
            pub mod bar;
            //- /foo/bar.rs
            pub struct Baz;
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn item_map_enum_importing() {
    covers!(item_map_enum_importing);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        enum E { V }
        use self::E::V;
        <|>
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
        E: t
        V: t v
        ",
    );
}

#[test]
fn item_map_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

#[test]
fn import_across_source_roots() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /lib.rs
        pub mod a {
            pub mod b {
                pub struct C;
            }
        }
    ",
    );
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let source_root = SourceRootId(1);

    let (sr2, pos) = db.add_fixture(
        source_root,
        "
        //- /main.rs
        use test_crate::a::b::C;
    ",
    );
    assert!(pos.is_none());

    let main_id = sr2.files[RelativePath::new("/main.rs")];

    eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            C: t v
            test_crate: t
        ",
    );
}

#[test]
fn reexport_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub use foo::Baz;

        mod foo;

        //- /foo.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
    let (mut db, pos) = MockDatabase::with_position(initial);
    let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
    let krate = module.krate(&db).unwrap();
    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(format!("{:?}", events).contains("item_map"))
    }
    db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));

    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(
            !format!("{:?}", events).contains("item_map"),
            "{:#?}",
            events
        )
    }
}

#[test]
fn typing_inside_a_function_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;<|>

        use crate::foo::bar::Baz;

        fn foo() -> i32 {
            1 + 1
        }
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        mod foo;

        use crate::foo::bar::Baz;

        fn foo() -> i32 { 92 }
        ",
    );
}

#[test]
fn adding_inner_items_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        struct S { a: i32}
        enum E { A }
        trait T {
            fn a() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
        }
        use crate::foo::bar::Baz;
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        struct S { a: i32, b: () }
        enum E { A, B }
        trait T {
            fn a() {}
            fn b() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
            fn b() {}
        }
        use crate::foo::bar::Baz;
        ",
    );
}

#[test]
fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        <|>
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 1 + 1 }
            }
        }
        ",
        "
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 92 }
            }
        }
        ",
    );
}
<commit_msg>Add test for extern crate renames<commit_after>use std::sync::Arc;

use ra_db::{CrateGraph, SourceRootId, SourceDatabase};
use relative_path::RelativePath;
use test_utils::{assert_eq_text, covers};

use crate::{
    ItemMap,
    PersistentHirDatabase,
    mock::MockDatabase,
    module_tree::ModuleId,
};
use super::Resolution;

fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (db, pos) = MockDatabase::with_position(fixture);
    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

/// Sets the crate root to the file of the cursor marker
fn item_map_custom_crate_root(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (mut db, pos) = MockDatabase::with_position(fixture);

    let mut crate_graph = CrateGraph::default();
    crate_graph.add_crate_root(pos.file_id);
    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
    let mut lines = map[module_id]
        .items
        .iter()
        .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
        .collect::<Vec<_>>();
    lines.sort();
    let actual = lines.join("\n");
    let expected = expected
        .trim()
        .lines()
        .map(|it| it.trim())
        .collect::<Vec<_>>()
        .join("\n");
    assert_eq_text!(&expected, &actual);

    fn dump_resolution(resolution: &Resolution) -> &'static str {
        match (
            resolution.def.types.is_some(),
            resolution.def.values.is_some(),
        ) {
            (true, true) => "t v",
            (true, false) => "t",
            (false, true) => "v",
            (false, false) => "_",
        }
    }
}

#[test]
fn item_map_smoke_test() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn use_as() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::Baz as Foo;
        <|>

        //- /foo/mod.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Foo: t v
            foo: t
        ",
    );
}

#[test]
fn use_trees() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::{Baz, Quux};
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        pub enum Quux {};
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            Quux: t
            foo: t
        ",
    );
}

#[test]
fn re_exports() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn module_resolution_works_for_non_standard_filenames() {
    let (item_map, module_id) = item_map_custom_crate_root(
        "
        //- /my_library.rs
        mod foo;
        use self::foo::Bar;
        <|>
        //- /foo/mod.rs
        pub struct Bar;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Bar: t v
            foo: t
        ",
    );
}

#[test]
fn name_res_works_for_broken_modules() {
    covers!(name_res_works_for_broken_modules);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo // no `;`, no body

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: _
        ",
    );
}

#[test]
fn item_map_contains_items_from_expansions() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        salsa::query_group! {
            trait Baz {}
        }
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t
            foo: t
        ",
    );
}

#[test]
fn item_map_using_self() {
    let (item_map, module_id) = item_map(
        "
            //- /lib.rs
            mod foo;
            use crate::foo::bar::Baz::{self};
            <|>
            //- /foo/mod.rs
            pub mod bar;
            //- /foo/bar.rs
            pub struct Baz;
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn item_map_enum_importing() {
    covers!(item_map_enum_importing);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        enum E { V }
        use self::E::V;
        <|>
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
        E: t
        V: t v
        ",
    );
}

#[test]
fn item_map_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

#[test]
fn extern_crate_rename() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        extern crate alloc as alloc_crate;

        mod alloc;

        use alloc_crate::Arc;

        //- /lib.rs
        struct Arc;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "alloc".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Arc: t v
            alloc: t
            alloc_crate: t
        ",
    );
}

#[test]
fn import_across_source_roots() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /lib.rs
        pub mod a {
            pub mod b {
                pub struct C;
            }
        }
    ",
    );
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let source_root = SourceRootId(1);

    let (sr2, pos) = db.add_fixture(
        source_root,
        "
        //- /main.rs
        use test_crate::a::b::C;
    ",
    );
    assert!(pos.is_none());

    let main_id = sr2.files[RelativePath::new("/main.rs")];

    eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            C: t v
            test_crate: t
        ",
    );
}

#[test]
fn reexport_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub use foo::Baz;

        mod foo;

        //- /foo.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
    let (mut db, pos) = MockDatabase::with_position(initial);
    let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
    let krate = module.krate(&db).unwrap();
    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(format!("{:?}", events).contains("item_map"))
    }
    db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));

    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(
            !format!("{:?}", events).contains("item_map"),
            "{:#?}",
            events
        )
    }
}

#[test]
fn typing_inside_a_function_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;<|>

        use crate::foo::bar::Baz;

        fn foo() -> i32 {
            1 + 1
        }
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        mod foo;

        use crate::foo::bar::Baz;

        fn foo() -> i32 { 92 }
        ",
    );
}

#[test]
fn adding_inner_items_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        struct S { a: i32}
        enum E { A }
        trait T {
            fn a() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
        }
        use crate::foo::bar::Baz;
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        struct S { a: i32, b: () }
        enum E { A, B }
        trait T {
            fn a() {}
            fn b() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
            fn b() {}
        }
        use crate::foo::bar::Baz;
        ",
    );
}

#[test]
fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        <|>
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 1 + 1 }
            }
        }
        ",
        "
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 92 }
            }
        }
        ",
    );
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use ra_db::{CrateGraph, SourceRootId, SourceDatabase};
use relative_path::RelativePath;
use test_utils::{assert_eq_text, covers};

use crate::{
    ItemMap,
    PersistentHirDatabase,
    mock::MockDatabase,
    module_tree::ModuleId,
};
use super::Resolution;

fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (db, pos) = MockDatabase::with_position(fixture);
    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

/// Sets the crate root to the file of the cursor marker
fn item_map_custom_crate_root(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (mut db, pos) = MockDatabase::with_position(fixture);

    let mut crate_graph = CrateGraph::default();
    crate_graph.add_crate_root(pos.file_id);
    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
    let mut lines = map[module_id]
        .items
        .iter()
        .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
        .collect::<Vec<_>>();
    lines.sort();
    let actual = lines.join("\n");
    let expected = expected
        .trim()
        .lines()
        .map(|it| it.trim())
        .collect::<Vec<_>>()
        .join("\n");
    assert_eq_text!(&expected, &actual);

    fn dump_resolution(resolution: &Resolution) -> &'static str {
        match (
            resolution.def.types.is_some(),
            resolution.def.values.is_some(),
        ) {
            (true, true) => "t v",
            (true, false) => "t",
            (false, true) => "v",
            (false, false) => "_",
        }
    }
}

#[test]
fn item_map_smoke_test() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn use_trees() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::{Baz, Quux};
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        pub enum Quux {};
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            Quux: t
            foo: t
        ",
    );
}

#[test]
fn re_exports() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn module_resolution_works_for_non_standard_filenames() {
    let (item_map, module_id) = item_map_custom_crate_root(
        "
        //- /my_library.rs
        mod foo;
        use self::foo::Bar;
        <|>
        //- /foo/mod.rs
        pub struct Bar;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Bar: t v
            foo: t
        ",
    );
}

#[test]
fn name_res_works_for_broken_modules() {
    covers!(name_res_works_for_broken_modules);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo // no `;`, no body

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: _
        ",
    );
}

#[test]
fn item_map_contains_items_from_expansions() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        salsa::query_group! {
            trait Baz {}
        }
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t
            foo: t
        ",
    );
}

#[test]
fn item_map_using_self() {
    let (item_map, module_id) = item_map(
        "
            //- /lib.rs
            mod foo;
            use crate::foo::bar::Baz::{self};
            <|>
            //- /foo/mod.rs
            pub mod bar;
            //- /foo/bar.rs
            pub struct Baz;
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn item_map_enum_importing() {
    covers!(item_map_enum_importing);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        enum E { V }
        use self::E::V;
        <|>
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
        E: t
        V: t v
        ",
    );
}

#[test]
fn item_map_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

#[test]
fn import_across_source_roots() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /lib.rs
        pub mod a {
            pub mod b {
                pub struct C;
            }
        }
    ",
    );
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let source_root = SourceRootId(1);

    let (sr2, pos) = db.add_fixture(
        source_root,
        "
        //- /main.rs
        use test_crate::a::b::C;
    ",
    );
    assert!(pos.is_none());

    let main_id = sr2.files[RelativePath::new("/main.rs")];

    eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            C: t v
            test_crate: t
        ",
    );
}

#[test]
fn reexport_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub use foo::Baz;

        mod foo;

        //- /foo.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
    let (mut db, pos) = MockDatabase::with_position(initial);
    let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
    let krate = module.krate(&db).unwrap();
    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(format!("{:?}", events).contains("item_map"))
    }
    db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));

    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(
            !format!("{:?}", events).contains("item_map"),
            "{:#?}",
            events
        )
    }
}

#[test]
fn typing_inside_a_function_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;<|>

        use crate::foo::bar::Baz;

        fn foo() -> i32 {
            1 + 1
        }
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        mod foo;

        use crate::foo::bar::Baz;

        fn foo() -> i32 { 92 }
        ",
    );
}

#[test]
fn adding_inner_items_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        struct S { a: i32}
        enum E { A }
        trait T {
            fn a() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
        }
        use crate::foo::bar::Baz;
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        struct S { a: i32, b: () }
        enum E { A, B }
        trait T {
            fn a() {}
            fn b() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
            fn b() {}
        }
        use crate::foo::bar::Baz;
        ",
    );
}

#[test]
fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        <|>
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 1 + 1 }
            }
        }
        ",
        "
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 92 }
            }
        }
        ",
    );
}
<commit_msg>Add test for `use as`<commit_after>use std::sync::Arc;

use ra_db::{CrateGraph, SourceRootId, SourceDatabase};
use relative_path::RelativePath;
use test_utils::{assert_eq_text, covers};

use crate::{
    ItemMap,
    PersistentHirDatabase,
    mock::MockDatabase,
    module_tree::ModuleId,
};
use super::Resolution;

fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (db, pos) = MockDatabase::with_position(fixture);
    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

/// Sets the crate root to the file of the cursor marker
fn item_map_custom_crate_root(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
    let (mut db, pos) = MockDatabase::with_position(fixture);

    let mut crate_graph = CrateGraph::default();
    crate_graph.add_crate_root(pos.file_id);
    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_position(&db, pos).unwrap();
    let krate = module.krate(&db).unwrap();
    let module_id = module.module_id;
    (db.item_map(krate), module_id)
}

fn check_module_item_map(map: &ItemMap, module_id: ModuleId, expected: &str) {
    let mut lines = map[module_id]
        .items
        .iter()
        .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
        .collect::<Vec<_>>();
    lines.sort();
    let actual = lines.join("\n");
    let expected = expected
        .trim()
        .lines()
        .map(|it| it.trim())
        .collect::<Vec<_>>()
        .join("\n");
    assert_eq_text!(&expected, &actual);

    fn dump_resolution(resolution: &Resolution) -> &'static str {
        match (
            resolution.def.types.is_some(),
            resolution.def.values.is_some(),
        ) {
            (true, true) => "t v",
            (true, false) => "t",
            (false, true) => "v",
            (false, false) => "_",
        }
    }
}

#[test]
fn item_map_smoke_test() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn use_as() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::Baz as Foo;
        <|>

        //- /foo/mod.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Foo: t v
            foo: t
        ",
    );
}

#[test]
fn use_trees() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::{Baz, Quux};
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        pub enum Quux {};
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            Quux: t
            foo: t
        ",
    );
}

#[test]
fn re_exports() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn module_resolution_works_for_non_standard_filenames() {
    let (item_map, module_id) = item_map_custom_crate_root(
        "
        //- /my_library.rs
        mod foo;
        use self::foo::Bar;
        <|>
        //- /foo/mod.rs
        pub struct Bar;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Bar: t v
            foo: t
        ",
    );
}

#[test]
fn name_res_works_for_broken_modules() {
    covers!(name_res_works_for_broken_modules);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo // no `;`, no body

        use self::foo::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        pub use self::bar::Baz;

        //- /foo/bar.rs
        pub struct Baz;
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: _
        ",
    );
}

#[test]
fn item_map_contains_items_from_expansions() {
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;
        <|>

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        salsa::query_group! {
            trait Baz {}
        }
    ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t
            foo: t
        ",
    );
}

#[test]
fn item_map_using_self() {
    let (item_map, module_id) = item_map(
        "
            //- /lib.rs
            mod foo;
            use crate::foo::bar::Baz::{self};
            <|>
            //- /foo/mod.rs
            pub mod bar;
            //- /foo/bar.rs
            pub struct Baz;
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
            Baz: t v
            foo: t
        ",
    );
}

#[test]
fn item_map_enum_importing() {
    covers!(item_map_enum_importing);
    let (item_map, module_id) = item_map(
        "
        //- /lib.rs
        enum E { V }
        use self::E::V;
        <|>
        ",
    );
    check_module_item_map(
        &item_map,
        module_id,
        "
        E: t
        V: t v
        ",
    );
}

#[test]
fn item_map_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

#[test]
fn import_across_source_roots() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /lib.rs
        pub mod a {
            pub mod b {
                pub struct C;
            }
        }
    ",
    );
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let source_root = SourceRootId(1);

    let (sr2, pos) = db.add_fixture(
        source_root,
        "
        //- /main.rs
        use test_crate::a::b::C;
    ",
    );
    assert!(pos.is_none());

    let main_id = sr2.files[RelativePath::new("/main.rs")];

    eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            C: t v
            test_crate: t
        ",
    );
}

#[test]
fn reexport_across_crates() {
    let (mut db, sr) = MockDatabase::with_files(
        "
        //- /main.rs
        use test_crate::Baz;

        //- /lib.rs
        pub use foo::Baz;

        mod foo;

        //- /foo.rs
        pub struct Baz;
    ",
    );
    let main_id = sr.files[RelativePath::new("/main.rs")];
    let lib_id = sr.files[RelativePath::new("/lib.rs")];

    let mut crate_graph = CrateGraph::default();
    let main_crate = crate_graph.add_crate_root(main_id);
    let lib_crate = crate_graph.add_crate_root(lib_id);
    crate_graph
        .add_dep(main_crate, "test_crate".into(), lib_crate)
        .unwrap();

    db.set_crate_graph(Arc::new(crate_graph));

    let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
    let krate = module.krate(&db).unwrap();
    let item_map = db.item_map(krate);

    check_module_item_map(
        &item_map,
        module.module_id,
        "
            Baz: t v
            test_crate: t
        ",
    );
}

fn check_item_map_is_not_recomputed(initial: &str, file_change: &str) {
    let (mut db, pos) = MockDatabase::with_position(initial);
    let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
    let krate = module.krate(&db).unwrap();
    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(format!("{:?}", events).contains("item_map"))
    }
    db.set_file_text(pos.file_id, Arc::new(file_change.to_string()));

    {
        let events = db.log_executed(|| {
            db.item_map(krate);
        });
        assert!(
            !format!("{:?}", events).contains("item_map"),
            "{:#?}",
            events
        )
    }
}

#[test]
fn typing_inside_a_function_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;<|>

        use crate::foo::bar::Baz;

        fn foo() -> i32 {
            1 + 1
        }
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        mod foo;

        use crate::foo::bar::Baz;

        fn foo() -> i32 { 92 }
        ",
    );
}

#[test]
fn adding_inner_items_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        struct S { a: i32}
        enum E { A }
        trait T {
            fn a() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
        }
        use crate::foo::bar::Baz;
        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        pub struct Baz;
        ",
        "
        struct S { a: i32, b: () }
        enum E { A, B }
        trait T {
            fn a() {}
            fn b() {}
        }
        mod foo;<|>
        impl S {
            fn a() {}
            fn b() {}
        }
        use crate::foo::bar::Baz;
        ",
    );
}

#[test]
fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() {
    check_item_map_is_not_recomputed(
        "
        //- /lib.rs
        mod foo;

        use crate::foo::bar::Baz;

        //- /foo/mod.rs
        pub mod bar;

        //- /foo/bar.rs
        <|>
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 1 + 1 }
            }
        }
        ",
        "
        salsa::query_group! {
            trait Baz {
                fn foo() -> i32 { 92 }
            }
        }
        ",
    );
}
<|endoftext|>


<commit_before>extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate reqwest;

use std::env;
use std::fs::File;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader};

struct Indexer;

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn end_headers(&mut self) -> Skip {
        Skip(true)
    }

    fn end_game(&mut self, game: &'pgn [u8]) {
        let mut res = reqwest::Client::new()
            .put("http://localhost:9000/import/master")
            .body(game.to_owned())
            .send().expect("send game");

        let mut answer = String::new();
        res.read_to_string(&mut answer).expect("decode response");
        println!("-> {}", answer);
        assert!(res.status().is_success());
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing master games from {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        Reader::new(&mut Indexer, &pgn[..]).read_all();
    }
}
<commit_msg>let master importer skip errors<commit_after>extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate reqwest;

use std::env;
use std::fs::File;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader};

struct Indexer;

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn end_headers(&mut self) -> Skip {
        Skip(true)
    }

    fn end_game(&mut self, game: &'pgn [u8]) {
        let mut res = reqwest::Client::new()
            .put("http://localhost:9000/import/master")
            .body(game.to_owned())
            .send().expect("send game");

        let mut answer = String::new();
        res.read_to_string(&mut answer).expect("decode response");
        println!("-> {}", answer);
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing master games from {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        Reader::new(&mut Indexer, &pgn[..]).read_all();
    }
}
<|endoftext|>