<commit_before>// Copyright 2014 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ext::quote::rt::ToTokens;
use syntax::print::pprust;
use syntax::ast;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, MacResult, MacItem};
use ast::*;
use utility::*;

struct ToTokensVec<'a, T>
{
  v: &'a Vec<T>
}

impl<'a, T: ToTokens> ToTokens for ToTokensVec<'a, T>
{
  fn to_tokens(&self, cx: &ExtCtxt) -> Vec<ast::TokenTree> {
    let mut tts = Vec::new();
    for e in self.v.iter() {
      tts = tts.append(e.to_tokens(cx).as_slice());
    }
    tts
  }
}

pub struct PegCompiler<'a>
{
  top_level_items: Vec<ast::P<ast::Item>>,
  cx: &'a ExtCtxt<'a>,
  unique_id: uint,
  grammar: &'a Peg,
  current_rule_idx: uint,
  starting_rule: uint
}

impl<'a> PegCompiler<'a>
{
  pub fn compile(cx: &'a ExtCtxt, grammar: &'a Peg) -> Box<MacResult>
  {
    let mut compiler = PegCompiler{
      top_level_items: Vec::new(),
      cx: cx,
      unique_id: 0,
      grammar: grammar,
      current_rule_idx: 0,
      starting_rule: 0
    };
    compiler.compile_peg()
  }

  fn compile_peg(&mut self) -> Box<MacResult>
  {
    let grammar_name = self.grammar.name;

    let peg_lib = self.compile_peg_library();

    for rule in self.grammar.rules.iter() {
      self.compile_rule_attributes(&rule.attributes);
      let rule_name = rule.name;
      let rule_def = self.compile_rule_rhs(&rule.def);
      self.top_level_items.push(quote_item!(self.cx,
        fn $rule_name (input: &str, pos: uint) -> Result<uint, String>
        {
          $rule_def
        }
      ).unwrap());
      self.current_rule_idx += 1;
    }

    let parse_fn = self.compile_entry_point();
    self.compile_peg_library();

    let items = ToTokensVec{v: &self.top_level_items};

    let grammar = quote_item!(self.cx,
      pub mod $grammar_name
      {
        $peg_lib
        $parse_fn
        $items
      }
    ).unwrap();

    match get_attribute(&self.grammar.attributes, "print_generated") {
      Some(_) => self.cx.parse_sess.span_diagnostic.handler.note(
        pprust::item_to_string(grammar).as_slice()),
      None => ()
    }

    MacItem::new(grammar)
  }

  fn compile_rule_attributes(&mut self, attrs: &Vec<Attribute>)
  {
    match get_attribute(attrs, "start") {
      Some(_) => self.starting_rule = self.current_rule_idx,
      _ => ()
    }
  }

  fn compile_function(&mut self, fun_name: &Ident, body: &ast::P<ast::Expr>) -> ast::P<ast::Item>
  {
    (quote_item!(self.cx,
      pub fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        $body
      }
    )).unwrap()
  }

  fn compile_lib_any_single_char(&mut self) -> ast::P<ast::Item>
  {
    let cx = self.cx;
    let fun_name = token::gensym_ident("any_single_char");
    self.compile_function(&fun_name, &quote_expr!(cx,
      if input.len() - pos > 0 {
        Ok(input.char_range_at(pos).next)
      } else {
        Err(format!("End of input when matching `.`"))
      }
    ))
  }

  fn compile_lib_match_literal(&mut self) -> ast::P<ast::Item>
  {
    (quote_item!(self.cx,
      pub fn match_literal<'a, 'b>(input: &'a str, pos: uint, lit: &'a str, lit_len: uint)
        -> Result<uint, String>
      {
        if input.len() - pos == 0 {
          Err(format!("End of input when matching the literal `{}`", lit))
        } else if input.slice_from(pos).starts_with(lit) {
          Ok(pos + lit_len)
        } else {
          Err(format!("Expected `{}` but got `{}`", lit, input.slice_from(pos)))
        }
      })).unwrap()
  }

  fn compile_peg_library(&mut self) -> ast::P<ast::Item>
  {
    let any_single_char = self.compile_lib_any_single_char();
    let match_literal = self.compile_lib_match_literal();
    (quote_item!(self.cx,
      pub mod peg {
        $any_single_char
        $match_literal
      }
    )).unwrap()
  }

  fn compile_entry_point(&mut self) -> ast::P<ast::Item>
  {
    let start_idx = self.starting_rule;
    let start_rule = self.grammar.rules.as_slice()[start_idx].name;
    (quote_item!(self.cx,
      pub fn parse<'a>(input: &'a str) -> Result<Option<&'a str>, String>
      {
        match $start_rule(input, 0) {
          Ok(pos) => {
            assert!(pos <= input.len())
            if pos == input.len() {
              Ok(None)
            } else {
              Ok(Some(input.slice_from(pos)))
            }
          },
          Err(msg) => Err(msg)
        }
      })).unwrap()
  }

  fn compile_rule_rhs(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    match &expr.node {
      &StrLiteral(ref lit_str) => {
        self.compile_str_literal(lit_str)
      },
      &AnySingleChar => {
        self.compile_any_single_char()
      },
      &NonTerminalSymbol(id) => {
        self.compile_non_terminal_symbol(id)
      },
      &Sequence(ref seq) => {
        self.compile_sequence(seq.as_slice())
      },
      &Choice(ref choices) => {
        self.compile_choice(choices.as_slice())
      },
      &ZeroOrMore(ref e) => {
        self.compile_zero_or_more(e)
      },
      &OneOrMore(ref e) => {
        self.compile_one_or_more(e)
      },
      &Optional(ref e) => {
        self.compile_optional(e)
      },
      &NotPredicate(ref e) => {
        self.compile_not_predicate(e)
      },
      &AndPredicate(ref e) => {
        self.compile_and_predicate(e)
      },
      &CharacterClass(ref e) => {
        self.compile_character_class(e)
      }
    }
  }

  fn compile_non_terminal_symbol(&mut self, id: Ident) -> ast::P<ast::Expr>
  {
    quote_expr!(self.cx,
      $id(input, pos)
    )
  }

  fn compile_any_single_char(&mut self) -> ast::P<ast::Expr>
  {
    quote_expr!(self.cx, peg::any_single_char(input, pos))
  }

  fn compile_str_literal(&mut self, lit_str: &String) -> ast::P<ast::Expr>
  {
    let lit_str = lit_str.as_slice();
    let lit_len = lit_str.len();
    quote_expr!(self.cx,
      peg::match_literal(input, pos, $lit_str, $lit_len)
    )
  }

  fn map_foldr_expr<'a>(&mut self, seq: &'a [Box<Expression>],
    f: |ast::P<ast::Expr>, ast::P<ast::Expr>| -> ast::P<ast::Expr>) -> ast::P<ast::Expr>
  {
    assert!(seq.len() > 0);
    let mut seq_it = seq
      .iter()
      .map(|e| { self.compile_rule_rhs(e) })
      .rev();

    let head = seq_it.next().unwrap();
    seq_it.fold(head, f)
  }

  fn compile_sequence<'a>(&mut self, seq: &'a [Box<Expression>]) -> ast::P<ast::Expr>
  {
    let cx = self.cx;
    self.map_foldr_expr(seq, |tail, head| {
      quote_expr!(cx,
        match $head {
          Ok(pos) => {
            $tail
          }
          x => x
        }
      )
    })
  }

  fn compile_choice<'a>(&mut self, choices: &'a [Box<Expression>]) -> ast::P<ast::Expr>
  {
    let cx = self.cx;
    self.map_foldr_expr(choices, |tail, head| {
      quote_expr!(cx,
        match $head {
          Err(msg) => {
            $tail
          }
          x => x
        }
      )
    })
  }

  fn gen_uid(&mut self) -> uint
  {
    self.unique_id += 1;
    self.unique_id - 1
  }

  fn current_rule_name(&self) -> String
  {
    id_to_string(self.grammar.rules.as_slice()[self.current_rule_idx].name)
  }

  fn gensym<'a>(&mut self, prefix: &'a str) -> Ident
  {
    token::gensym_ident(format!(
      "{}_{}_{}", prefix,
        self.current_rule_name(),
        self.gen_uid()).as_slice())
  }

  fn compile_star(&mut self, expr: &ast::P<ast::Expr>) -> ast::P<ast::Expr>
  {
    let fun_name = self.gensym("star");
    let cx = self.cx;
    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        let mut npos = pos;
        while npos < input.len() {
          let pos = npos;
          match $expr {
            Ok(pos) => {
              npos = pos;
            },
            _ => break
          }
        }
        Ok(npos)
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }

  fn compile_zero_or_more(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    self.compile_star(&expr)
  }

  fn compile_one_or_more(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    let star_fn = self.compile_star(&expr);
    let fun_name = self.gensym("plus");
    let cx = self.cx;
    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        match $expr {
          Ok(pos) => $star_fn,
          x => x
        }
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }

  fn compile_optional(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(pos) => Ok(pos),
        _ => Ok(pos)
      }
    )
  }

  fn compile_not_predicate(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(_) => Err(format!("An `!expr` failed.")),
        _ => Ok(pos)
    })
  }

  fn compile_and_predicate(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(_) => Ok(pos),
        x => x
    })
  }

  fn compile_character_class(&mut self, expr: &CharacterClassExpr) -> ast::P<ast::Expr>
  {
    let fun_name = self.gensym("class_char");
    let cx = self.cx;
    assert!(expr.intervals.len() > 0);

    let mut seq_it = expr.intervals.iter();

    let CharacterInterval{lo:lo, hi:hi} = *seq_it.next().unwrap();
    let cond = seq_it.fold(quote_expr!(cx, (current >= $lo && current <= $hi)), |accu, &CharacterInterval{lo:lo, hi:hi}| {
      quote_expr!(cx, $accu || (current >= $lo && current <= $hi))
    });

    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        let current = input.char_range_at(pos).ch;
        if $cond {
          Ok(input.char_range_at(pos).next)
        } else {
          Err(format!("It doesn't match the character class."))
        }
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }
}
<commit_msg>[Attribute] Allow dead code and non snake case name in the generated code.<commit_after>// Copyright 2014 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ext::quote::rt::ToTokens;
use syntax::print::pprust;
use syntax::ast;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, MacResult, MacItem};
use ast::*;
use utility::*;

struct ToTokensVec<'a, T>
{
  v: &'a Vec<T>
}

impl<'a, T: ToTokens> ToTokens for ToTokensVec<'a, T>
{
  fn to_tokens(&self, cx: &ExtCtxt) -> Vec<ast::TokenTree> {
    let mut tts = Vec::new();
    for e in self.v.iter() {
      tts = tts.append(e.to_tokens(cx).as_slice());
    }
    tts
  }
}

pub struct PegCompiler<'a>
{
  top_level_items: Vec<ast::P<ast::Item>>,
  cx: &'a ExtCtxt<'a>,
  unique_id: uint,
  grammar: &'a Peg,
  current_rule_idx: uint,
  starting_rule: uint
}

impl<'a> PegCompiler<'a>
{
  pub fn compile(cx: &'a ExtCtxt, grammar: &'a Peg) -> Box<MacResult>
  {
    let mut compiler = PegCompiler{
      top_level_items: Vec::new(),
      cx: cx,
      unique_id: 0,
      grammar: grammar,
      current_rule_idx: 0,
      starting_rule: 0
    };
    compiler.compile_peg()
  }

  fn compile_peg(&mut self) -> Box<MacResult>
  {
    let grammar_name = self.grammar.name;

    let peg_lib = self.compile_peg_library();

    for rule in self.grammar.rules.iter() {
      self.compile_rule_attributes(&rule.attributes);
      let rule_name = rule.name;
      let rule_def = self.compile_rule_rhs(&rule.def);
      self.top_level_items.push(quote_item!(self.cx,
        fn $rule_name (input: &str, pos: uint) -> Result<uint, String>
        {
          $rule_def
        }
      ).unwrap());
      self.current_rule_idx += 1;
    }

    let parse_fn = self.compile_entry_point();
    self.compile_peg_library();

    let items = ToTokensVec{v: &self.top_level_items};

    let grammar = quote_item!(self.cx,
      pub mod $grammar_name
      {
        #![allow(dead_code)]
        #![allow(non_snake_case_functions)]

        $peg_lib
        $parse_fn
        $items
      }
    ).unwrap();

    match get_attribute(&self.grammar.attributes, "print_generated") {
      Some(_) => self.cx.parse_sess.span_diagnostic.handler.note(
        pprust::item_to_string(grammar).as_slice()),
      None => ()
    }

    MacItem::new(grammar)
  }

  fn compile_rule_attributes(&mut self, attrs: &Vec<Attribute>)
  {
    match get_attribute(attrs, "start") {
      Some(_) => self.starting_rule = self.current_rule_idx,
      _ => ()
    }
  }

  fn compile_function(&mut self, fun_name: &Ident, body: &ast::P<ast::Expr>) -> ast::P<ast::Item>
  {
    (quote_item!(self.cx,
      pub fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        $body
      }
    )).unwrap()
  }

  fn compile_lib_any_single_char(&mut self) -> ast::P<ast::Item>
  {
    let cx = self.cx;
    let fun_name = token::gensym_ident("any_single_char");
    self.compile_function(&fun_name, &quote_expr!(cx,
      if input.len() - pos > 0 {
        Ok(input.char_range_at(pos).next)
      } else {
        Err(format!("End of input when matching `.`"))
      }
    ))
  }

  fn compile_lib_match_literal(&mut self) -> ast::P<ast::Item>
  {
    (quote_item!(self.cx,
      pub fn match_literal<'a, 'b>(input: &'a str, pos: uint, lit: &'a str, lit_len: uint)
        -> Result<uint, String>
      {
        if input.len() - pos == 0 {
          Err(format!("End of input when matching the literal `{}`", lit))
        } else if input.slice_from(pos).starts_with(lit) {
          Ok(pos + lit_len)
        } else {
          Err(format!("Expected `{}` but got `{}`", lit, input.slice_from(pos)))
        }
      })).unwrap()
  }

  fn compile_peg_library(&mut self) -> ast::P<ast::Item>
  {
    let any_single_char = self.compile_lib_any_single_char();
    let match_literal = self.compile_lib_match_literal();
    (quote_item!(self.cx,
      pub mod peg {
        $any_single_char
        $match_literal
      }
    )).unwrap()
  }

  fn compile_entry_point(&mut self) -> ast::P<ast::Item>
  {
    let start_idx = self.starting_rule;
    let start_rule = self.grammar.rules.as_slice()[start_idx].name;
    (quote_item!(self.cx,
      pub fn parse<'a>(input: &'a str) -> Result<Option<&'a str>, String>
      {
        match $start_rule(input, 0) {
          Ok(pos) => {
            assert!(pos <= input.len())
            if pos == input.len() {
              Ok(None)
            } else {
              Ok(Some(input.slice_from(pos)))
            }
          },
          Err(msg) => Err(msg)
        }
      })).unwrap()
  }

  fn compile_rule_rhs(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    match &expr.node {
      &StrLiteral(ref lit_str) => {
        self.compile_str_literal(lit_str)
      },
      &AnySingleChar => {
        self.compile_any_single_char()
      },
      &NonTerminalSymbol(id) => {
        self.compile_non_terminal_symbol(id)
      },
      &Sequence(ref seq) => {
        self.compile_sequence(seq.as_slice())
      },
      &Choice(ref choices) => {
        self.compile_choice(choices.as_slice())
      },
      &ZeroOrMore(ref e) => {
        self.compile_zero_or_more(e)
      },
      &OneOrMore(ref e) => {
        self.compile_one_or_more(e)
      },
      &Optional(ref e) => {
        self.compile_optional(e)
      },
      &NotPredicate(ref e) => {
        self.compile_not_predicate(e)
      },
      &AndPredicate(ref e) => {
        self.compile_and_predicate(e)
      },
      &CharacterClass(ref e) => {
        self.compile_character_class(e)
      }
    }
  }

  fn compile_non_terminal_symbol(&mut self, id: Ident) -> ast::P<ast::Expr>
  {
    quote_expr!(self.cx,
      $id(input, pos)
    )
  }

  fn compile_any_single_char(&mut self) -> ast::P<ast::Expr>
  {
    quote_expr!(self.cx, peg::any_single_char(input, pos))
  }

  fn compile_str_literal(&mut self, lit_str: &String) -> ast::P<ast::Expr>
  {
    let lit_str = lit_str.as_slice();
    let lit_len = lit_str.len();
    quote_expr!(self.cx,
      peg::match_literal(input, pos, $lit_str, $lit_len)
    )
  }

  fn map_foldr_expr<'a>(&mut self, seq: &'a [Box<Expression>],
    f: |ast::P<ast::Expr>, ast::P<ast::Expr>| -> ast::P<ast::Expr>) -> ast::P<ast::Expr>
  {
    assert!(seq.len() > 0);
    let mut seq_it = seq
      .iter()
      .map(|e| { self.compile_rule_rhs(e) })
      .rev();

    let head = seq_it.next().unwrap();
    seq_it.fold(head, f)
  }

  fn compile_sequence<'a>(&mut self, seq: &'a [Box<Expression>]) -> ast::P<ast::Expr>
  {
    let cx = self.cx;
    self.map_foldr_expr(seq, |tail, head| {
      quote_expr!(cx,
        match $head {
          Ok(pos) => {
            $tail
          }
          x => x
        }
      )
    })
  }

  fn compile_choice<'a>(&mut self, choices: &'a [Box<Expression>]) -> ast::P<ast::Expr>
  {
    let cx = self.cx;
    self.map_foldr_expr(choices, |tail, head| {
      quote_expr!(cx,
        match $head {
          Err(_) => {
            $tail
          }
          x => x
        }
      )
    })
  }

  fn gen_uid(&mut self) -> uint
  {
    self.unique_id += 1;
    self.unique_id - 1
  }

  fn current_rule_name(&self) -> String
  {
    id_to_string(self.grammar.rules.as_slice()[self.current_rule_idx].name)
  }

  fn gensym<'a>(&mut self, prefix: &'a str) -> Ident
  {
    token::gensym_ident(format!(
      "{}_{}_{}", prefix,
        self.current_rule_name(),
        self.gen_uid()).as_slice())
  }

  fn compile_star(&mut self, expr: &ast::P<ast::Expr>) -> ast::P<ast::Expr>
  {
    let fun_name = self.gensym("star");
    let cx = self.cx;
    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        let mut npos = pos;
        while npos < input.len() {
          let pos = npos;
          match $expr {
            Ok(pos) => {
              npos = pos;
            },
            _ => break
          }
        }
        Ok(npos)
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }

  fn compile_zero_or_more(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    self.compile_star(&expr)
  }

  fn compile_one_or_more(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    let star_fn = self.compile_star(&expr);
    let fun_name = self.gensym("plus");
    let cx = self.cx;
    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        match $expr {
          Ok(pos) => $star_fn,
          x => x
        }
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }

  fn compile_optional(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(pos) => Ok(pos),
        _ => Ok(pos)
      }
    )
  }

  fn compile_not_predicate(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(_) => Err(format!("An `!expr` failed.")),
        _ => Ok(pos)
    })
  }

  fn compile_and_predicate(&mut self, expr: &Box<Expression>) -> ast::P<ast::Expr>
  {
    let expr = self.compile_rule_rhs(expr);
    quote_expr!(self.cx,
      match $expr {
        Ok(_) => Ok(pos),
        x => x
    })
  }

  fn compile_character_class(&mut self, expr: &CharacterClassExpr) -> ast::P<ast::Expr>
  {
    let fun_name = self.gensym("class_char");
    let cx = self.cx;
    assert!(expr.intervals.len() > 0);

    let mut seq_it = expr.intervals.iter();

    let CharacterInterval{lo:lo, hi:hi} = *seq_it.next().unwrap();
    let cond = seq_it.fold(quote_expr!(cx, (current >= $lo && current <= $hi)), |accu, &CharacterInterval{lo:lo, hi:hi}| {
      quote_expr!(cx, $accu || (current >= $lo && current <= $hi))
    });

    self.top_level_items.push(quote_item!(cx,
      fn $fun_name<'a>(input: &'a str, pos: uint) -> Result<uint, String>
      {
        let current = input.char_range_at(pos).ch;
        if $cond {
          Ok(input.char_range_at(pos).next)
        } else {
          Err(format!("It doesn't match the character class."))
        }
      }
    ).unwrap());
    quote_expr!(self.cx, $fun_name(input, pos))
  }
}
<|endoftext|>


<commit_before>use crate::{Config, LevelPadding};
use chrono;
use log::{LevelFilter, Record};
use std::io::{Error, Write};
use std::thread;

#[inline(always)]
pub fn try_log<W>(config: &Config, record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    if should_skip(config, record) {
        return Ok(());
    }

    if config.time <= record.level() && config.time != LevelFilter::Off {
        write_time(write, config)?;
    }

    if config.level <= record.level() && config.level != LevelFilter::Off {
        write_level(record, write, config)?;
    }

    if config.thread <= record.level() && config.thread != LevelFilter::Off {
        write_thread_id(write)?;
    }

    if config.target <= record.level() && config.target != LevelFilter::Off {
        write_target(record, write)?;
    }

    if config.location <= record.level() && config.location != LevelFilter::Off {
        write_location(record, write)?;
    }

    write_args(record, write)
}

#[inline(always)]
pub fn write_time<W>(write: &mut W, config: &Config) -> Result<(), Error>
where
    W: Write + Sized,
{
    let cur_time = if config.time_local {
        (chrono::Local::now() + config.time_offset).format(&*config.time_format)
    } else {
        (chrono::Utc::now() + config.time_offset).format(&*config.time_format)
    };

    write!(write, "{} ", cur_time)?;
    Ok(())
}

#[inline(always)]
pub fn write_level<W>(record: &Record<'_>, write: &mut W, config: &Config) -> Result<(), Error>
where
    W: Write + Sized,
{
    match config.level_padding {
        LevelPadding::Left => write!(write, "[{: >5}] ", record.level())?,
        LevelPadding::Right => write!(write, "[{: <5}] ", record.level())?,
        LevelPadding::Off => write!(write, "[{}] ", record.level())?,
    };
    Ok(())
}

#[inline(always)]
pub fn write_target<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    write!(write, "{}: ", record.target())?;
    Ok(())
}

#[inline(always)]
pub fn write_location<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    let file = record.file().unwrap_or("<unknown>");
    if let Some(line) = record.line() {
        write!(write, "[{}:{}] ", file, line)?;
    } else {
        write!(write, "[{}:<unknown>] ", file)?;
    }
    Ok(())
}

pub fn write_thread_id<W>(write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    let id = format!("{:?}", thread::current().id());
    let id = id.replace("ThreadId(", "");
    let id = id.replace(")", "");
    write!(write, "({}) ", id)?;
    Ok(())
}

#[inline(always)]
pub fn write_args<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    writeln!(write, "{}", record.args())?;
    Ok(())
}

#[inline(always)]
pub fn should_skip(config: &Config, record: &Record<'_>) -> bool {
    // If a module path and allowed list are available
    match (record.module_path(), &*config.filter_allow) {
        (Some(path), allowed) if allowed.len() > 0 => {
            // Check that the module path matches at least one allow filter
            if let None = allowed.iter().find(|v| path.starts_with(&***v)) {
                // If not, skip any further writing
                return true;
            }
        }
        _ => {}
    }

    // If a module path and ignore list are available
    match (record.module_path(), &*config.filter_ignore) {
        (Some(path), ignore) if ignore.len() > 0 => {
            // Check that the module path does not match any ignore filters
            if let Some(_) = ignore.iter().find(|v| path.starts_with(&***v)) {
                // If not, skip any further writing
                return true;
            }
        }
        _ => {}
    }

    return false;
}
<commit_msg>Changed allow and ignore filters to look at target instead of module_path<commit_after>use crate::{Config, LevelPadding};
use chrono;
use log::{LevelFilter, Record};
use std::io::{Error, Write};
use std::thread;

#[inline(always)]
pub fn try_log<W>(config: &Config, record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    if should_skip(config, record) {
        return Ok(());
    }

    if config.time <= record.level() && config.time != LevelFilter::Off {
        write_time(write, config)?;
    }

    if config.level <= record.level() && config.level != LevelFilter::Off {
        write_level(record, write, config)?;
    }

    if config.thread <= record.level() && config.thread != LevelFilter::Off {
        write_thread_id(write)?;
    }

    if config.target <= record.level() && config.target != LevelFilter::Off {
        write_target(record, write)?;
    }

    if config.location <= record.level() && config.location != LevelFilter::Off {
        write_location(record, write)?;
    }

    write_args(record, write)
}

#[inline(always)]
pub fn write_time<W>(write: &mut W, config: &Config) -> Result<(), Error>
where
    W: Write + Sized,
{
    let cur_time = if config.time_local {
        (chrono::Local::now() + config.time_offset).format(&*config.time_format)
    } else {
        (chrono::Utc::now() + config.time_offset).format(&*config.time_format)
    };

    write!(write, "{} ", cur_time)?;
    Ok(())
}

#[inline(always)]
pub fn write_level<W>(record: &Record<'_>, write: &mut W, config: &Config) -> Result<(), Error>
where
    W: Write + Sized,
{
    match config.level_padding {
        LevelPadding::Left => write!(write, "[{: >5}] ", record.level())?,
        LevelPadding::Right => write!(write, "[{: <5}] ", record.level())?,
        LevelPadding::Off => write!(write, "[{}] ", record.level())?,
    };
    Ok(())
}

#[inline(always)]
pub fn write_target<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    write!(write, "{}: ", record.target())?;
    Ok(())
}

#[inline(always)]
pub fn write_location<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    let file = record.file().unwrap_or("<unknown>");
    if let Some(line) = record.line() {
        write!(write, "[{}:{}] ", file, line)?;
    } else {
        write!(write, "[{}:<unknown>] ", file)?;
    }
    Ok(())
}

pub fn write_thread_id<W>(write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    let id = format!("{:?}", thread::current().id());
    let id = id.replace("ThreadId(", "");
    let id = id.replace(")", "");
    write!(write, "({}) ", id)?;
    Ok(())
}

#[inline(always)]
pub fn write_args<W>(record: &Record<'_>, write: &mut W) -> Result<(), Error>
where
    W: Write + Sized,
{
    writeln!(write, "{}", record.args())?;
    Ok(())
}

#[inline(always)]
pub fn should_skip(config: &Config, record: &Record<'_>) -> bool {
    // If a module path and allowed list are available
    match (record.target(), &*config.filter_allow) {
        (path, allowed) if allowed.len() > 0 => {
            // Check that the module path matches at least one allow filter
            if let None = allowed.iter().find(|v| path.starts_with(&***v)) {
                // If not, skip any further writing
                return true;
            }
        }
        _ => {}
    }

    // If a module path and ignore list are available
    match (record.target(), &*config.filter_ignore) {
        (path, ignore) if ignore.len() > 0 => {
            // Check that the module path does not match any ignore filters
            if let Some(_) = ignore.iter().find(|v| path.starts_with(&***v)) {
                // If not, skip any further writing
                return true;
            }
        }
        _ => {}
    }

    return false;
}
<|endoftext|>


<commit_before>// https://github.com/NationalSecurityAgency/ghidra/tree/79d8f164f8bb8b15cfb60c5d4faeb8e1c25d15ca/Ghidra/Processors/x86/data/patterns
#![allow(non_snake_case)]
use anyhow::Result;
use regex::bytes::Regex;

use crate::{aspace::AddressSpace, loader::pe::PE, VA};

lazy_static! {
    static ref PATTERNS: Regex = {

        // CC debug filler, x86win_patterns.xml#L4
        let CC = r"\xCC";
        // multiple CC filler bytes, x86win_patterns.xml#L5
        let CCCC = r"\xCC\xCC";
        // NOP filler, x86win_patterns.xml#L6
        let NOP = r"\x90";
        // RET filler, x86win_patterns.xml#L7
        let RET = r"\xC3";
        // LEAVE RET, x86win_patterns.xml#L8
        let LEAVE_RET = r"\xC9\xC3";

        // x86win_patterns.xml#L9
        // 0xC2 ......00 0x00
        // let RET_LONGFORM =

        let PREPATTERN = format!("(?P<prepattern>{})", vec![
            CC,
            CCCC,
            NOP,
            RET,
            LEAVE_RET,
        ].join("|"));

        // PUSH EBP; MOV EBP, ESP, x86win_patterns.xml#L12
        let P0 = r"\x55\x8B\xEC";

        // x86win_patterns.xml#L13
        //  SUB ESP, #small
        // <data>0x83ec 0.....00 </data>
        //let P1 = r"\x83\xEC (
        //  \x00|\x04|\x08|\x0c|\x10|\x14|\x18|\x1c|
        //  \x20|\x24|\x28|\x2c|\x30|\x34|\x38|\x3c|
        //  \x40|\x44|\x48|\x4c|\x50|\x54|\x58|\x5c|
        //  \x60|\x64|\x68|\x6c|\x70|\x74|\x78|\x7c)";

        // x86win_patterns.xml#L14
        //  PUSH-1; PUSH FUNC; MOV EAX, FS[0]
        // <data>0x6aff68........64a100000000 </data>
        //let P2 = r"\x6A\xFF\x68....\x64\xA1\x00\x00\x00\x00";

        // x86win_patterns.xml#L15
        //   PUSH ESI; MOV ESI, ECX
        // <data>0x568bf1 </data>
        //let P3 = r"\x56\x8B\xF1";

        // x86win_patterns.xml#L16
        //   MOV EAX, ??; CALL; SUB ESP
        // <data>0xb8........e8........ 100000.1 0xec</data>
        // let P4 = r"";

        // x86win_patterns.xml#L17
        //    MOV EAX, ??; CALL
        // <data>0xb8........e8</data>
        // let P5 = r"";

        // x86win_patterns.xml#L18
        //    MOV EDI,EDI : PUSH EBP : MOV EBP,ESP
        // <data>0x8bff558bec</data>
        let P6 = r"\x8B\xFF\x55\x8B\xEC";

        // x86win_patterns.xml#L20
        //  PUSH EBX : MOV EBX,E*X
        // <data>0x538b 110110..</data>
        // let P7 = r"";

        // x86win_patterns.xml#L21
        //   PUSH EBX : PUSH ESI : PUSH EDI
        // <data>0x535657</data>
        // let P8 = r"";

        // x86win_patterns.xml#L22
        //   PUSH EBX : PUSH EBP : PUSH ESI
        // <data>0x535556</data>
        // let P9 = r"";

        // x86win_patterns.xml#L23
        //  PUSH EBX : PUSH ESI : PUSH ECX
        // <data>0x535651</data>
        // let P10 = r"";

        // x86win_patterns.xml#L25
        //   PUSH EBX : PUSH ESI : MOV ESI,EDX
        // <data>0x53568bf2</data>
        // let P11 = r"";

        // x86win_patterns.xml#L26
        //   PUSH EBX : PUSH ESI : MOV EBX,EAX
        // <data>0x53568bd8</data>
        // let P12 = r"";

        // x86win_patterns.xml#L27
        //   PUSH EBX : PUSH ESI : MOV ESI,ECX
        // <data>0x53568bf1</data>
        // let P13 = r"";

        // x86win_patterns.xml#L28
        //   PUSH EBX : PUSH ESI : MOV EBX,EDX
        // <data>0x53568bda</data>
        // let P14 = r"";

        // x86win_patterns.xml#L29
        //   PUSH EBX : PUSH ESI : MOV ESI,EAX
        // <data>0x53568bf0</data>
        // let P15 = r"";

        // x86win_patterns.xml#L30
        //   PUSH ESI : PUSH EDI : MOV EDI,ECX
        // <data>0x56578bf9</data>
        // let P16 = r"";

        // x86win_patterns.xml#L31
        //   PUSH ESI : PUSH EDI : MOV ESI,ECX
        // <data>0x56578bf1</data>
        // let P17 = r"";

        let POSTPATTERN = format!("(?P<postpattern>{})", vec![
            P0,
            P6,
        ].join("|"));

        let re = format!(
            r"(?x)   # whitespace allowed
              (?-u)  # disable unicode mode, so we can match raw bytes
              (:?{})   # capture the pre match
              (:?{})   # capture the match
            ",
            PREPATTERN,
            POSTPATTERN,
            );

        Regex::new(&re).unwrap()
    };
}

#[allow(dead_code)]
const INDEX_ALL: usize = 0;
#[allow(dead_code)]
const INDEX_PREMATCH: usize = 2;
const INDEX_MATCH: usize = 4;

pub fn find_function_prologues(pe: &PE) -> Result<Vec<VA>> {
    let mut ret = vec![];
    for section in pe.executable_sections() {
        let vstart: VA = section.virtual_range.start;
        let vsize = (section.virtual_range.end - section.virtual_range.start) as usize;
        let sec_buf = pe.module.address_space.read_bytes(vstart, vsize)?;

        for capture in PATTERNS.captures_iter(&sec_buf) {
            let m = capture.get(INDEX_MATCH).unwrap();
            let va = vstart + m.start() as u64;
            ret.push(va);
        }
    }
    Ok(ret)
}
<commit_msg>patterns: add additional prepattern<commit_after>// https://github.com/NationalSecurityAgency/ghidra/tree/79d8f164f8bb8b15cfb60c5d4faeb8e1c25d15ca/Ghidra/Processors/x86/data/patterns
#![allow(non_snake_case)]
use anyhow::Result;
use regex::bytes::Regex;

use crate::{aspace::AddressSpace, loader::pe::PE, VA};

lazy_static! {
    static ref PATTERNS: Regex = {

        // CC debug filler, x86win_patterns.xml#L4
        let CC = r"\xCC";
        // multiple CC filler bytes, x86win_patterns.xml#L5
        let CCCC = r"\xCC\xCC";
        // NOP filler, x86win_patterns.xml#L6
        let NOP = r"\x90";
        // RET filler, x86win_patterns.xml#L7
        let RET = r"\xC3";
        // LEAVE RET, x86win_patterns.xml#L8
        let LEAVE_RET = r"\xC9\xC3";

        // JMP DWORD PTR ds:????????
        // mimikatz:0x46B674
        let JMP_FAR = r"\xFF\x25....";

        // x86win_patterns.xml#L9
        // 0xC2 ......00 0x00
        // let RET_LONGFORM =

        let PREPATTERN = format!("(?P<prepattern>{})", vec![
            CC,
            CCCC,
            NOP,
            RET,
            LEAVE_RET,
            JMP_FAR,
        ].join("|"));

        // PUSH EBP; MOV EBP, ESP, x86win_patterns.xml#L12
        let P0 = r"\x55\x8B\xEC";

        // x86win_patterns.xml#L13
        //  SUB ESP, #small
        // <data>0x83ec 0.....00 </data>
        //let P1 = r"\x83\xEC (
        //  \x00|\x04|\x08|\x0c|\x10|\x14|\x18|\x1c|
        //  \x20|\x24|\x28|\x2c|\x30|\x34|\x38|\x3c|
        //  \x40|\x44|\x48|\x4c|\x50|\x54|\x58|\x5c|
        //  \x60|\x64|\x68|\x6c|\x70|\x74|\x78|\x7c)";

        // x86win_patterns.xml#L14
        //  PUSH-1; PUSH FUNC; MOV EAX, FS[0]
        // <data>0x6aff68........64a100000000 </data>
        //let P2 = r"\x6A\xFF\x68....\x64\xA1\x00\x00\x00\x00";

        // x86win_patterns.xml#L15
        //   PUSH ESI; MOV ESI, ECX
        // <data>0x568bf1 </data>
        //let P3 = r"\x56\x8B\xF1";

        // x86win_patterns.xml#L16
        //   MOV EAX, ??; CALL; SUB ESP
        // <data>0xb8........e8........ 100000.1 0xec</data>
        // let P4 = r"";

        // x86win_patterns.xml#L17
        //    MOV EAX, ??; CALL
        // <data>0xb8........e8</data>
        // let P5 = r"";

        // x86win_patterns.xml#L18
        //    MOV EDI,EDI : PUSH EBP : MOV EBP,ESP
        // <data>0x8bff558bec</data>
        let P6 = r"\x8B\xFF\x55\x8B\xEC";

        // x86win_patterns.xml#L20
        //  PUSH EBX : MOV EBX,E*X
        // <data>0x538b 110110..</data>
        // let P7 = r"";

        // x86win_patterns.xml#L21
        //   PUSH EBX : PUSH ESI : PUSH EDI
        // <data>0x535657</data>
        // let P8 = r"";

        // x86win_patterns.xml#L22
        //   PUSH EBX : PUSH EBP : PUSH ESI
        // <data>0x535556</data>
        // let P9 = r"";

        // x86win_patterns.xml#L23
        //  PUSH EBX : PUSH ESI : PUSH ECX
        // <data>0x535651</data>
        // let P10 = r"";

        // x86win_patterns.xml#L25
        //   PUSH EBX : PUSH ESI : MOV ESI,EDX
        // <data>0x53568bf2</data>
        // let P11 = r"";

        // x86win_patterns.xml#L26
        //   PUSH EBX : PUSH ESI : MOV EBX,EAX
        // <data>0x53568bd8</data>
        // let P12 = r"";

        // x86win_patterns.xml#L27
        //   PUSH EBX : PUSH ESI : MOV ESI,ECX
        // <data>0x53568bf1</data>
        // let P13 = r"";

        // x86win_patterns.xml#L28
        //   PUSH EBX : PUSH ESI : MOV EBX,EDX
        // <data>0x53568bda</data>
        // let P14 = r"";

        // x86win_patterns.xml#L29
        //   PUSH EBX : PUSH ESI : MOV ESI,EAX
        // <data>0x53568bf0</data>
        // let P15 = r"";

        // x86win_patterns.xml#L30
        //   PUSH ESI : PUSH EDI : MOV EDI,ECX
        // <data>0x56578bf9</data>
        // let P16 = r"";

        // x86win_patterns.xml#L31
        //   PUSH ESI : PUSH EDI : MOV ESI,ECX
        // <data>0x56578bf1</data>
        // let P17 = r"";

        let POSTPATTERN = format!("(?P<postpattern>{})", vec![
            P0,
            P6,
        ].join("|"));

        let re = format!(
            r"(?x)   # whitespace allowed
              (?-u)  # disable unicode mode, so we can match raw bytes
              (:?{})   # capture the pre match
              (:?{})   # capture the match
            ",
            PREPATTERN,
            POSTPATTERN,
            );

        Regex::new(&re).unwrap()
    };
}

#[allow(dead_code)]
const INDEX_ALL: usize = 0;
#[allow(dead_code)]
const INDEX_PREMATCH: usize = 2;
const INDEX_MATCH: usize = 4;

pub fn find_function_prologues(pe: &PE) -> Result<Vec<VA>> {
    let mut ret = vec![];
    for section in pe.executable_sections() {
        let vstart: VA = section.virtual_range.start;
        let vsize = (section.virtual_range.end - section.virtual_range.start) as usize;
        let sec_buf = pe.module.address_space.read_bytes(vstart, vsize)?;

        for capture in PATTERNS.captures_iter(&sec_buf) {
            let m = capture.get(INDEX_MATCH).unwrap();
            let va = vstart + m.start() as u64;
            ret.push(va);
        }
    }
    Ok(ret)
}
<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;

use clap::{App, ArgMatches};
use hyper::client::{Client, RedirectPolicy};
use hyper::client::request::Request;
use hyper::header::{UserAgent, ContentLength};
use hyper::method::Method;
use url::Url;
use serde_json;

use CliResult;
use commands::Config;

#[derive(Debug, Serialize, Deserialize)]
struct Asset {
    browser_download_url: String,
    name: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Release {
    tag_name: String,
    assets: Vec<Asset>,
}

struct ReleaseInfo {
    version: String,
    download_url: Url,
}

fn get_asset_name() -> CliResult<String> {
    let p = Command::new("uname").arg("-sm").output()?;
    let output = String::from_utf8(p.stdout)?;
    let mut iter = output.trim().split(' ');
    let platform = iter.next().unwrap_or("unknown");
    let arch = iter.next().unwrap_or("unknown");
    Ok(format!("sentry-cli-{}-{}", platform, arch))
}

fn get_latest_release() -> CliResult<ReleaseInfo> {
    let ref_name = get_asset_name()?;

    let mut req = Request::new(Method::Get,
        Url::parse("https://api.github.com/repos/getsentry/sentry-cli/releases/latest")?)?;
    {
        let mut headers = req.headers_mut();
        headers.set(UserAgent("sentry-cli".into()));
    }
    let mut resp = req.start()?.send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }

    let rv : Release = serde_json::from_reader(&mut resp)?;

    for asset in rv.assets {
        if asset.name == ref_name {
            return Ok(ReleaseInfo {
                version: rv.tag_name,
                download_url: Url::parse(&asset.browser_download_url)?
            });
        }
    }

    fail!("Could not find download URL for updates.");
}

fn locate_executable_for_update() -> CliResult<PathBuf> {
    let exe = env::current_exe()?;
    let meta = fs::metadata(&exe)?;
    if meta.permissions().readonly() {
        fail!("The executable at '{}' cannot be changed.", exe.display());
    }
    Ok(exe)
}

fn download_url<P: AsRef<Path>>(url: &Url, dst: P) -> CliResult<()> {
    let mut client = Client::new();
    client.set_redirect_policy(RedirectPolicy::FollowAll);
    let mut resp = client.get(url.to_owned()).send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }
    let content_length = match resp.headers.get::<ContentLength>() {
        Some(&ContentLength(length)) => length,
        None => 0
    };

    print!("Downloading...");
    let mut last_status = 0;
    let mut buffer = [0; 4096];
    let mut downloaded = 0;
    let mut f = fs::File::create(dst)?;
    loop {
        let chunk_size = resp.read(&mut buffer)?;
        if chunk_size == 0 {
            break;
        } else {
            f.write(&buffer[..chunk_size])?;
            downloaded += chunk_size as u64;
        }
        if content_length > 0 {
            let status = downloaded * 100 / content_length;
            if status != last_status {
                print!("\rDownloading... {}%", status);
                last_status = status;
            }
        }
    }
    println!("");
    println!("Done!");
    Ok(())
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("update the sentry-cli executable")
}

pub fn execute<'a>(_matches: &ArgMatches<'a>, _config: &Config) -> CliResult<()> {
    let exe = locate_executable_for_update()?;
    let download_base = exe.parent().ok_or("Could not determine parent directory")?;
    let latest_release = get_latest_release()?;
    let tmp_path = download_base.join(".sentry-cli.part");
    let current_version = env!("CARGO_PKG_VERSION");
    println!("Latest release is {}", latest_release.version);

    if latest_release.version == current_version {
        println!("Already up to date!");
        return Ok(());
    }

    println!("Executable location: {}", exe.display());
    println!("Download location: {}", tmp_path.display());
    println!("Download URL: {}", latest_release.download_url);

    match download_url(&latest_release.download_url, &tmp_path) {
        Err(err) => {
            fs::remove_file(tmp_path).ok();
            return Err(err);
        },
        Ok(()) => {},
    }

    let mut perm = fs::metadata(&tmp_path)?.permissions();
    perm.set_mode(0o755);
    fs::set_permissions(&tmp_path, perm)?;
    fs::rename(&tmp_path, &exe)?;
    println!("Updated!");

    Ok(())
}
<commit_msg>Added sudo support<commit_after>use std::fs;
use std::env;
use std::path::Path;
use std::process::Command;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;

use clap::{App, ArgMatches};
use hyper::client::{Client, RedirectPolicy};
use hyper::client::request::Request;
use hyper::header::{UserAgent, ContentLength};
use hyper::method::Method;
use url::Url;
use serde_json;

use CliResult;
use commands::Config;

#[derive(Debug, Serialize, Deserialize)]
struct Asset {
    browser_download_url: String,
    name: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Release {
    tag_name: String,
    assets: Vec<Asset>,
}

struct ReleaseInfo {
    version: String,
    download_url: Url,
}

fn get_asset_name() -> CliResult<String> {
    let p = Command::new("uname").arg("-sm").output()?;
    let output = String::from_utf8(p.stdout)?;
    let mut iter = output.trim().split(' ');
    let platform = iter.next().unwrap_or("unknown");
    let arch = iter.next().unwrap_or("unknown");
    Ok(format!("sentry-cli-{}-{}", platform, arch))
}

fn get_latest_release() -> CliResult<ReleaseInfo> {
    let ref_name = get_asset_name()?;

    let mut req = Request::new(Method::Get,
        Url::parse("https://api.github.com/repos/getsentry/sentry-cli/releases/latest")?)?;
    {
        let mut headers = req.headers_mut();
        headers.set(UserAgent("sentry-cli".into()));
    }
    let mut resp = req.start()?.send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }

    let rv : Release = serde_json::from_reader(&mut resp)?;

    for asset in rv.assets {
        if asset.name == ref_name {
            return Ok(ReleaseInfo {
                version: rv.tag_name,
                download_url: Url::parse(&asset.browser_download_url)?
            });
        }
    }

    fail!("Could not find download URL for updates.");
}

fn download_url<P: AsRef<Path>>(url: &Url, dst: P) -> CliResult<()> {
    let mut client = Client::new();
    client.set_redirect_policy(RedirectPolicy::FollowAll);
    let mut resp = client.get(url.to_owned()).send()?;
    if !resp.status.is_success() {
        fail!(resp);
    }
    let content_length = match resp.headers.get::<ContentLength>() {
        Some(&ContentLength(length)) => length,
        None => 0
    };

    print!("Downloading...");
    let mut last_status = 0;
    let mut buffer = [0; 4096];
    let mut downloaded = 0;
    let mut f = fs::File::create(dst)?;
    loop {
        let chunk_size = resp.read(&mut buffer)?;
        if chunk_size == 0 {
            break;
        } else {
            f.write(&buffer[..chunk_size])?;
            downloaded += chunk_size as u64;
        }
        if content_length > 0 {
            let status = downloaded * 100 / content_length;
            if status != last_status {
                print!("\rDownloading... {}%", status);
                last_status = status;
            }
        }
    }
    println!("");
    println!("Done!");
    Ok(())
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("update the sentry-cli executable")
}

pub fn execute<'a>(_matches: &ArgMatches<'a>, _config: &Config) -> CliResult<()> {
    let exe = env::current_exe()?;
    let need_sudo = fs::metadata(&exe)?.permissions().readonly();
    let latest_release = get_latest_release()?;
    let tmp_path = if need_sudo {
        env::temp_dir().join(".sentry-cli.part")
    } else {
        exe.parent().unwrap().join(".sentry-cli.part")
    };
    let current_version = env!("CARGO_PKG_VERSION");
    println!("Latest release is {}", latest_release.version);

    if latest_release.version == current_version {
        println!("Already up to date!");
        return Ok(());
    }

    println!("Executable location: {}", exe.display());
    println!("Download URL: {}", latest_release.download_url);

    match download_url(&latest_release.download_url, &tmp_path) {
        Err(err) => {
            fs::remove_file(tmp_path).ok();
            return Err(err);
        },
        Ok(()) => {},
    }

    let mut perm = fs::metadata(&tmp_path)?.permissions();
    perm.set_mode(0o755);
    fs::set_permissions(&tmp_path, perm)?;

    if need_sudo {
        println!("Need to sudo to overwrite {}", exe.display());
        Command::new("sudo")
            .arg("-k")
            .arg("mv")
            .arg(&tmp_path)
            .arg(&exe)
            .status()?;
    } else {
        fs::rename(&tmp_path, &exe)?;
    }
    println!("Updated!");

    Ok(())
}
<|endoftext|>


<commit_before>//! This module implements import-resolution/macro expansion algorithm.
//!
//! The result of this module is `CrateDefMap`: a data structure which contains:
//!
//!   * a tree of modules for the crate
//!   * for each module, a set of items visible in the module (directly declared
//!     or imported)
//!
//! Note that `CrateDefMap` contains fully macro expanded code.
//!
//! Computing `CrateDefMap` can be partitioned into several logically
//! independent "phases". The phases are mutually recursive though, there's no
//! strict ordering.
//!
//! ## Collecting RawItems
//!
//! This happens in the `raw` module, which parses a single source file into a
//! set of top-level items. Nested imports are desugared to flat imports in this
//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
//! TokenTree).
//!
//! ## Collecting Modules
//!
//! This happens in the `collector` module. In this phase, we recursively walk
//! tree of modules, collect raw items from submodules, populate module scopes
//! with defined items (so, we assign item ids in this phase) and record the set
//! of unresolved imports and macros.
//!
//! While we walk tree of modules, we also record macro_rules definitions and
//! expand calls to macro_rules defined macros.
//!
//! ## Resolving Imports
//!
//! We maintain a list of currently unresolved imports. On every iteration, we
//! try to resolve some imports from this list. If the import is resolved, we
//! record it, by adding an item to current module scope and, if necessary, by
//! recursively populating glob imports.
//!
//! ## Resolving Macros
//!
//! macro_rules from the same crate use a global mutable namespace. We expand
//! them immediately, when we collect modules.
//!
//! Macros from other crates (including proc-macros) can be used with
//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
//! unexpanded macros. On every iteration, we try to resolve each macro call
//! path and, upon success, we run macro expansion and "collect module" phase on
//! the result

pub mod raw;
pub mod per_ns;
mod collector;
mod mod_resolution;
mod path_resolution;

#[cfg(test)]
mod tests;

use std::sync::Arc;

use hir_expand::{ast_id_map::FileAstId, diagnostics::DiagnosticSink, name::Name, MacroDefId};
use once_cell::sync::Lazy;
use ra_arena::Arena;
use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile;
use ra_syntax::ast;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    builtin_type::BuiltinType,
    db::DefDatabase2,
    nameres::{
        diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
    },
    path::Path,
    AstId, CrateModuleId, FunctionId, ImplId, ModuleDefId, ModuleId, TraitId,
};

/// Contains all top-level defs from a macro-expanded crate
#[derive(Debug, PartialEq, Eq)]
pub struct CrateDefMap {
    krate: CrateId,
    edition: Edition,
    /// The prelude module for this crate. This either comes from an import
    /// marked with the `prelude_import` attribute, or (in the normal case) from
    /// a dependency (`std` or `core`).
    prelude: Option<ModuleId>,
    extern_prelude: FxHashMap<Name, ModuleDefId>,
    root: CrateModuleId,
    modules: Arena<CrateModuleId, ModuleData>,

    /// Some macros are not well-behavior, which leads to infinite loop
    /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
    /// We mark it down and skip it in collector
    ///
    /// FIXME:
    /// Right now it only handle a poison macro in a single crate,
    /// such that if other crate try to call that macro,
    /// the whole process will do again until it became poisoned in that crate.
    /// We should handle this macro set globally
    /// However, do we want to put it as a global variable?
    poison_macros: FxHashSet<MacroDefId>,

    diagnostics: Vec<DefDiagnostic>,
}

impl std::ops::Index<CrateModuleId> for CrateDefMap {
    type Output = ModuleData;
    fn index(&self, id: CrateModuleId) -> &ModuleData {
        &self.modules[id]
    }
}

#[derive(Default, Debug, PartialEq, Eq)]
pub struct ModuleData {
    pub parent: Option<CrateModuleId>,
    pub children: FxHashMap<Name, CrateModuleId>,
    pub scope: ModuleScope,
    /// None for root
    pub declaration: Option<AstId<ast::Module>>,
    /// None for inline modules.
    ///
    /// Note that non-inline modules, by definition, live inside non-macro file.
    pub definition: Option<FileId>,
    pub impls: Vec<ImplId>,
}

#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub(crate) struct Declarations {
    fns: FxHashMap<FileAstId<ast::FnDef>, FunctionId>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct ModuleScope {
    items: FxHashMap<Name, Resolution>,
    /// Macros visable in current module in legacy textual scope
    ///
    /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
    /// If it yields no result, then it turns to module scoped `macros`.
    /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
    /// and only normal scoped `macros` will be searched in.
    ///
    /// Note that this automatically inherit macros defined textually before the definition of module itself.
    ///
    /// Module scoped macros will be inserted into `items` instead of here.
    // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
    // be all resolved to the last one defined if shadowing happens.
    legacy_macros: FxHashMap<Name, MacroDefId>,
}

static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
    BuiltinType::ALL
        .iter()
        .map(|(name, ty)| {
            (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
        })
        .collect()
});

/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
/// Other methods will only resolve values, types and module scoped macros only.
impl ModuleScope {
    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
        //FIXME: shadowing
        self.items.iter().chain(BUILTIN_SCOPE.iter())
    }

    /// Iterate over all module scoped macros
    pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
        self.items
            .iter()
            .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
    }

    /// Iterate over all legacy textual scoped macros visable at the end of the module
    pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
        self.legacy_macros.iter().map(|(name, def)| (name, *def))
    }

    /// Get a name from current module scope, legacy macros are not included
    pub fn get(&self, name: &Name) -> Option<&Resolution> {
        self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
    }

    pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
        self.items.values().filter_map(|r| match r.def.take_types() {
            Some(ModuleDefId::TraitId(t)) => Some(t),
            _ => None,
        })
    }

    fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
        self.legacy_macros.get(name).copied()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Resolution {
    /// None for unresolved
    pub def: PerNs,
    /// ident by which this is imported into local scope.
    pub import: Option<ImportId>,
}

impl CrateDefMap {
    pub(crate) fn crate_def_map_query(
        // Note that this doesn't have `+ AstDatabase`!
        // This gurantess that `CrateDefMap` is stable across reparses.
        db: &impl DefDatabase2,
        krate: CrateId,
    ) -> Arc<CrateDefMap> {
        let _p = profile("crate_def_map_query");
        let def_map = {
            let crate_graph = db.crate_graph();
            let edition = crate_graph.edition(krate);
            let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
            let root = modules.alloc(ModuleData::default());
            CrateDefMap {
                krate,
                edition,
                extern_prelude: FxHashMap::default(),
                prelude: None,
                root,
                modules,
                poison_macros: FxHashSet::default(),
                diagnostics: Vec::new(),
            }
        };
        let def_map = collector::collect_defs(db, def_map);
        Arc::new(def_map)
    }

    pub fn krate(&self) -> CrateId {
        self.krate
    }

    pub fn root(&self) -> CrateModuleId {
        self.root
    }

    pub fn prelude(&self) -> Option<ModuleId> {
        self.prelude
    }

    pub fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDefId> {
        &self.extern_prelude
    }

    pub fn add_diagnostics(
        &self,
        db: &impl DefDatabase2,
        module: CrateModuleId,
        sink: &mut DiagnosticSink,
    ) {
        self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
    }

    pub fn resolve_path(
        &self,
        db: &impl DefDatabase2,
        original_module: CrateModuleId,
        path: &Path,
    ) -> (PerNs, Option<usize>) {
        let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
        (res.resolved_def, res.segment_index)
    }

    pub fn modules(&self) -> impl Iterator<Item = CrateModuleId> + '_ {
        self.modules.iter().map(|(id, _data)| id)
    }

    pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = CrateModuleId> + '_ {
        self.modules
            .iter()
            .filter(move |(_id, data)| data.definition == Some(file_id))
            .map(|(id, _data)| id)
    }
}

mod diagnostics {
    use hir_expand::diagnostics::DiagnosticSink;
    use ra_db::RelativePathBuf;
    use ra_syntax::{ast, AstPtr};

    use crate::{db::DefDatabase2, diagnostics::UnresolvedModule, nameres::CrateModuleId, AstId};

    #[derive(Debug, PartialEq, Eq)]
    pub(super) enum DefDiagnostic {
        UnresolvedModule {
            module: CrateModuleId,
            declaration: AstId<ast::Module>,
            candidate: RelativePathBuf,
        },
    }

    impl DefDiagnostic {
        pub(super) fn add_to(
            &self,
            db: &impl DefDatabase2,
            target_module: CrateModuleId,
            sink: &mut DiagnosticSink,
        ) {
            match self {
                DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
                    if *module != target_module {
                        return;
                    }
                    let decl = declaration.to_node(db);
                    sink.push(UnresolvedModule {
                        file: declaration.file_id(),
                        decl: AstPtr::new(&decl),
                        candidate: candidate.clone(),
                    })
                }
            }
        }
    }
}
<commit_msg>Remove wrong clone<commit_after>//! This module implements import-resolution/macro expansion algorithm.
//!
//! The result of this module is `CrateDefMap`: a data structure which contains:
//!
//!   * a tree of modules for the crate
//!   * for each module, a set of items visible in the module (directly declared
//!     or imported)
//!
//! Note that `CrateDefMap` contains fully macro expanded code.
//!
//! Computing `CrateDefMap` can be partitioned into several logically
//! independent "phases". The phases are mutually recursive though, there's no
//! strict ordering.
//!
//! ## Collecting RawItems
//!
//! This happens in the `raw` module, which parses a single source file into a
//! set of top-level items. Nested imports are desugared to flat imports in this
//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
//! TokenTree).
//!
//! ## Collecting Modules
//!
//! This happens in the `collector` module. In this phase, we recursively walk
//! tree of modules, collect raw items from submodules, populate module scopes
//! with defined items (so, we assign item ids in this phase) and record the set
//! of unresolved imports and macros.
//!
//! While we walk tree of modules, we also record macro_rules definitions and
//! expand calls to macro_rules defined macros.
//!
//! ## Resolving Imports
//!
//! We maintain a list of currently unresolved imports. On every iteration, we
//! try to resolve some imports from this list. If the import is resolved, we
//! record it, by adding an item to current module scope and, if necessary, by
//! recursively populating glob imports.
//!
//! ## Resolving Macros
//!
//! macro_rules from the same crate use a global mutable namespace. We expand
//! them immediately, when we collect modules.
//!
//! Macros from other crates (including proc-macros) can be used with
//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
//! unexpanded macros. On every iteration, we try to resolve each macro call
//! path and, upon success, we run macro expansion and "collect module" phase on
//! the result

pub mod raw;
pub mod per_ns;
mod collector;
mod mod_resolution;
mod path_resolution;

#[cfg(test)]
mod tests;

use std::sync::Arc;

use hir_expand::{ast_id_map::FileAstId, diagnostics::DiagnosticSink, name::Name, MacroDefId};
use once_cell::sync::Lazy;
use ra_arena::Arena;
use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile;
use ra_syntax::ast;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    builtin_type::BuiltinType,
    db::DefDatabase2,
    nameres::{
        diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
    },
    path::Path,
    AstId, CrateModuleId, FunctionId, ImplId, ModuleDefId, ModuleId, TraitId,
};

/// Contains all top-level defs from a macro-expanded crate
#[derive(Debug, PartialEq, Eq)]
pub struct CrateDefMap {
    krate: CrateId,
    edition: Edition,
    /// The prelude module for this crate. This either comes from an import
    /// marked with the `prelude_import` attribute, or (in the normal case) from
    /// a dependency (`std` or `core`).
    prelude: Option<ModuleId>,
    extern_prelude: FxHashMap<Name, ModuleDefId>,
    root: CrateModuleId,
    modules: Arena<CrateModuleId, ModuleData>,

    /// Some macros are not well-behavior, which leads to infinite loop
    /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
    /// We mark it down and skip it in collector
    ///
    /// FIXME:
    /// Right now it only handle a poison macro in a single crate,
    /// such that if other crate try to call that macro,
    /// the whole process will do again until it became poisoned in that crate.
    /// We should handle this macro set globally
    /// However, do we want to put it as a global variable?
    poison_macros: FxHashSet<MacroDefId>,

    diagnostics: Vec<DefDiagnostic>,
}

impl std::ops::Index<CrateModuleId> for CrateDefMap {
    type Output = ModuleData;
    fn index(&self, id: CrateModuleId) -> &ModuleData {
        &self.modules[id]
    }
}

#[derive(Default, Debug, PartialEq, Eq)]
pub struct ModuleData {
    pub parent: Option<CrateModuleId>,
    pub children: FxHashMap<Name, CrateModuleId>,
    pub scope: ModuleScope,
    /// None for root
    pub declaration: Option<AstId<ast::Module>>,
    /// None for inline modules.
    ///
    /// Note that non-inline modules, by definition, live inside non-macro file.
    pub definition: Option<FileId>,
    pub impls: Vec<ImplId>,
}

#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct Declarations {
    fns: FxHashMap<FileAstId<ast::FnDef>, FunctionId>,
}

#[derive(Debug, Default, PartialEq, Eq)]
pub struct ModuleScope {
    items: FxHashMap<Name, Resolution>,
    /// Macros visable in current module in legacy textual scope
    ///
    /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first.
    /// If it yields no result, then it turns to module scoped `macros`.
    /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
    /// and only normal scoped `macros` will be searched in.
    ///
    /// Note that this automatically inherit macros defined textually before the definition of module itself.
    ///
    /// Module scoped macros will be inserted into `items` instead of here.
    // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
    // be all resolved to the last one defined if shadowing happens.
    legacy_macros: FxHashMap<Name, MacroDefId>,
}

static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
    BuiltinType::ALL
        .iter()
        .map(|(name, ty)| {
            (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
        })
        .collect()
});

/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
/// Other methods will only resolve values, types and module scoped macros only.
impl ModuleScope {
    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
        //FIXME: shadowing
        self.items.iter().chain(BUILTIN_SCOPE.iter())
    }

    /// Iterate over all module scoped macros
    pub fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
        self.items
            .iter()
            .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_)))
    }

    /// Iterate over all legacy textual scoped macros visable at the end of the module
    pub fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
        self.legacy_macros.iter().map(|(name, def)| (name, *def))
    }

    /// Get a name from current module scope, legacy macros are not included
    pub fn get(&self, name: &Name) -> Option<&Resolution> {
        self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name))
    }

    pub fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
        self.items.values().filter_map(|r| match r.def.take_types() {
            Some(ModuleDefId::TraitId(t)) => Some(t),
            _ => None,
        })
    }

    fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
        self.legacy_macros.get(name).copied()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Resolution {
    /// None for unresolved
    pub def: PerNs,
    /// ident by which this is imported into local scope.
    pub import: Option<ImportId>,
}

impl CrateDefMap {
    pub(crate) fn crate_def_map_query(
        // Note that this doesn't have `+ AstDatabase`!
        // This gurantess that `CrateDefMap` is stable across reparses.
        db: &impl DefDatabase2,
        krate: CrateId,
    ) -> Arc<CrateDefMap> {
        let _p = profile("crate_def_map_query");
        let def_map = {
            let crate_graph = db.crate_graph();
            let edition = crate_graph.edition(krate);
            let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
            let root = modules.alloc(ModuleData::default());
            CrateDefMap {
                krate,
                edition,
                extern_prelude: FxHashMap::default(),
                prelude: None,
                root,
                modules,
                poison_macros: FxHashSet::default(),
                diagnostics: Vec::new(),
            }
        };
        let def_map = collector::collect_defs(db, def_map);
        Arc::new(def_map)
    }

    pub fn krate(&self) -> CrateId {
        self.krate
    }

    pub fn root(&self) -> CrateModuleId {
        self.root
    }

    pub fn prelude(&self) -> Option<ModuleId> {
        self.prelude
    }

    pub fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDefId> {
        &self.extern_prelude
    }

    pub fn add_diagnostics(
        &self,
        db: &impl DefDatabase2,
        module: CrateModuleId,
        sink: &mut DiagnosticSink,
    ) {
        self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
    }

    pub fn resolve_path(
        &self,
        db: &impl DefDatabase2,
        original_module: CrateModuleId,
        path: &Path,
    ) -> (PerNs, Option<usize>) {
        let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
        (res.resolved_def, res.segment_index)
    }

    pub fn modules(&self) -> impl Iterator<Item = CrateModuleId> + '_ {
        self.modules.iter().map(|(id, _data)| id)
    }

    pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = CrateModuleId> + '_ {
        self.modules
            .iter()
            .filter(move |(_id, data)| data.definition == Some(file_id))
            .map(|(id, _data)| id)
    }
}

mod diagnostics {
    use hir_expand::diagnostics::DiagnosticSink;
    use ra_db::RelativePathBuf;
    use ra_syntax::{ast, AstPtr};

    use crate::{db::DefDatabase2, diagnostics::UnresolvedModule, nameres::CrateModuleId, AstId};

    #[derive(Debug, PartialEq, Eq)]
    pub(super) enum DefDiagnostic {
        UnresolvedModule {
            module: CrateModuleId,
            declaration: AstId<ast::Module>,
            candidate: RelativePathBuf,
        },
    }

    impl DefDiagnostic {
        pub(super) fn add_to(
            &self,
            db: &impl DefDatabase2,
            target_module: CrateModuleId,
            sink: &mut DiagnosticSink,
        ) {
            match self {
                DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
                    if *module != target_module {
                        return;
                    }
                    let decl = declaration.to_node(db);
                    sink.push(UnresolvedModule {
                        file: declaration.file_id(),
                        decl: AstPtr::new(&decl),
                        candidate: candidate.clone(),
                    })
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
pub struct Optimizer {}

impl Optimizer {
    pub fn new() -> Self {
        Optimizer {}
    }
}

pub trait OptIntf {
    fn zero_grad(&mut self) {}
    /* ignore largely unused closure arg to start */
    fn step(&mut self);
}
<commit_msg> start fleshing out optimizer interface<commit_after>pub use std::collections::HashMap;
pub use nn::ParamIter;

pub struct Optimizer<'a, T: Copy + 'a> {
    params: ParamIter<'a, T>,
    defaults: HashMap<&'static str, OptimOpts>,
}
#[derive(Clone)]
pub enum OptimOpts {
    Bool(bool),
    Int(i32),
    Float(f32),
}
impl<T: Copy> From<T> for OptimOpts {
    #[allow(unused_variables)]
    default fn from(input: T) -> Self {
        unreachable!()
    }
}
impl From<f32> for OptimOpts {
    fn from(input: f32) -> Self {
        OptimOpts::Float(input)
    }
}
impl From<i32> for OptimOpts {
    fn from(input: i32) -> Self {
        OptimOpts::Int(input)
    }
}
impl From<bool> for OptimOpts {
    fn from(input: bool) -> Self {
        OptimOpts::Bool(input)
    }
}

impl<'a, T: Copy + 'a> Optimizer<'a, T> {
    pub fn new(params: ParamIter<'a, T>, defaults: HashMap<&'static str, OptimOpts>) -> Self {
        Optimizer {
            params: params,
            defaults: defaults,
        }
    }
}

pub trait OptIntf<'a, T: Copy + 'a> {
    fn optimizer(&mut self) -> &mut Optimizer<'a, T>;
    fn zero_grad(&mut self) {
        let opt = self.optimizer();
    }
    /* ignore largely unused closure arg to start */
    fn step(&mut self);
}
<|endoftext|>