<commit_before>use futures::{Future, BoxFuture};
use linear_map::LinearMap;
use protobuf;

use mercury::MercuryError;
use protocol;
use session::Session;
use util::{SpotifyId, FileId, StrChunksExt};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
    where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
{
    let mut forbidden = "".to_string();
    let mut allowed = "".to_string();
    let rs = restrictions.into_iter().filter(|r|
        r.get_catalogue_str().contains(&catalogue.to_owned())
    );

    for r in rs {
        forbidden.push_str(r.get_countries_forbidden());
        allowed.push_str(r.get_countries_allowed());
    }

    (forbidden == "" || !countrylist_contains(forbidden.as_str(), country)) &&
    (allowed == "" || countrylist_contains(allowed.as_str(), country))
}

pub trait MetadataTrait : Send + 'static {
    type Message: protobuf::MessageStatic;

    fn base_url() -> &'static str;
    fn parse(msg: &Self::Message, session: &Session) -> Self;
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl MetadataTrait for Track {
    type Message = protocol::metadata::Track;

    fn base_url() -> &'static str {
        "hm://metadata/3/track"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let files = msg.get_file()
                       .iter()
                       .filter(|file| file.has_file_id())
                       .map(|file| {
                           let mut dst = [0u8; 20];
                           dst.clone_from_slice(file.get_file_id());
                           (file.get_format(), FileId(dst))
                       })
                       .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()),
            artists: artists,
            files: files,
            alternatives: msg.get_alternative()
                             .iter()
                             .map(|alt| SpotifyId::from_raw(alt.get_gid()))
                             .collect(),
            available: parse_restrictions(msg.get_restriction(),
                                          &country,
                                          "premium"),
        }
    }
}

impl MetadataTrait for Album {
    type Message = protocol::metadata::Album;

    fn base_url() -> &'static str {
        "hm://metadata/3/album"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let tracks = msg.get_disc()
                        .iter()
                        .flat_map(|disc| disc.get_track())
                        .filter(|track| track.has_gid())
                        .map(|track| SpotifyId::from_raw(track.get_gid()))
                        .collect::<Vec<_>>();

        let covers = msg.get_cover_group()
                        .get_image()
                        .iter()
                        .filter(|image| image.has_file_id())
                        .map(|image| {
                            let mut dst = [0u8; 20];
                            dst.clone_from_slice(image.get_file_id());
                            FileId(dst)
                        })
                        .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl MetadataTrait for Artist {
    type Message = protocol::metadata::Artist;

    fn base_url() -> &'static str {
        "hm://metadata/3/artist"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks = msg.get_top_track()
                            .iter()
                            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
                            .unwrap()
                            .get_track()
                            .iter()
                            .filter(|track| track.has_gid())
                            .map(|track| SpotifyId::from_raw(track.get_gid()))
                            .collect::<Vec<_>>();

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks
        }
    }
}

component! {
    MetadataManager : MetadataManagerInner { }
}

impl MetadataManager {
    pub fn get<T: MetadataTrait>(&self, id: SpotifyId) -> BoxFuture<T, MercuryError> {
        let session = self.session();

        let uri = format!("{}/{}", T::base_url(), id.to_base16());
        let request = session.mercury().get(uri);

        request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: T::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(T::parse(&msg, &session))
        }).boxed()
    }
}
<commit_msg>Update metadata.rs<commit_after>use futures::{Future, BoxFuture};
use linear_map::LinearMap;
use protobuf;

use mercury::MercuryError;
use protocol;
use session::Session;
use util::{SpotifyId, FileId, StrChunksExt};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
    where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
{
    let mut forbidden = "".to_string();
    let mut has_forbidden = false;

    let mut allowed = "".to_string();
    let mut has_allowed = false;

    let rs = restrictions.into_iter().filter(|r|
        r.get_catalogue_str().contains(&catalogue.to_owned())
    );

    for r in rs {
        if r.has_countries_forbidden() {
            forbidden.push_str(r.get_countries_forbidden());
            has_forbidden = true;
        }

        if r.has_countries_allowed() {
            allowed.push_str(r.get_countries_allowed());
            has_allowed = true;
        }
    }

    (!has_forbidden || !countrylist_contains(forbidden.as_str(), country)) &&
    (!has_allowed || countrylist_contains(allowed.as_str(), country))
}

pub trait MetadataTrait : Send + 'static {
    type Message: protobuf::MessageStatic;

    fn base_url() -> &'static str;
    fn parse(msg: &Self::Message, session: &Session) -> Self;
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl MetadataTrait for Track {
    type Message = protocol::metadata::Track;

    fn base_url() -> &'static str {
        "hm://metadata/3/track"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let files = msg.get_file()
                       .iter()
                       .filter(|file| file.has_file_id())
                       .map(|file| {
                           let mut dst = [0u8; 20];
                           dst.clone_from_slice(file.get_file_id());
                           (file.get_format(), FileId(dst))
                       })
                       .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()),
            artists: artists,
            files: files,
            alternatives: msg.get_alternative()
                             .iter()
                             .map(|alt| SpotifyId::from_raw(alt.get_gid()))
                             .collect(),
            available: parse_restrictions(msg.get_restriction(),
                                          &country,
                                          "premium"),
        }
    }
}

impl MetadataTrait for Album {
    type Message = protocol::metadata::Album;

    fn base_url() -> &'static str {
        "hm://metadata/3/album"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg.get_artist()
                         .iter()
                         .filter(|artist| artist.has_gid())
                         .map(|artist| SpotifyId::from_raw(artist.get_gid()))
                         .collect::<Vec<_>>();

        let tracks = msg.get_disc()
                        .iter()
                        .flat_map(|disc| disc.get_track())
                        .filter(|track| track.has_gid())
                        .map(|track| SpotifyId::from_raw(track.get_gid()))
                        .collect::<Vec<_>>();

        let covers = msg.get_cover_group()
                        .get_image()
                        .iter()
                        .filter(|image| image.has_file_id())
                        .map(|image| {
                            let mut dst = [0u8; 20];
                            dst.clone_from_slice(image.get_file_id());
                            FileId(dst)
                        })
                        .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl MetadataTrait for Artist {
    type Message = protocol::metadata::Artist;

    fn base_url() -> &'static str {
        "hm://metadata/3/artist"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks = msg.get_top_track()
                            .iter()
                            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
                            .unwrap()
                            .get_track()
                            .iter()
                            .filter(|track| track.has_gid())
                            .map(|track| SpotifyId::from_raw(track.get_gid()))
                            .collect::<Vec<_>>();

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks
        }
    }
}

component! {
    MetadataManager : MetadataManagerInner { }
}

impl MetadataManager {
    pub fn get<T: MetadataTrait>(&self, id: SpotifyId) -> BoxFuture<T, MercuryError> {
        let session = self.session();

        let uri = format!("{}/{}", T::base_url(), id.to_base16());
        let request = session.mercury().get(uri);

        request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: T::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(T::parse(&msg, &session))
        }).boxed()
    }
}
<|endoftext|>


<commit_before>use routing;
use rpc;
use bus;

use hash::Hash;
use bincode::serde;
use std::{net, io, thread, time};
use std::sync;
use std::sync::{Weak, Arc};

pub const SOCKET_BUFFER_SIZE_BYTES : usize = 65536;
pub const SOCKET_TIMEOUT_S         : u64   = 5;
pub const UPDATE_BUS_SIZE_BYTES    : usize = 50;

/// Subotai node.
///
/// On construction, a detached thread for packet reception is
/// launched.
pub struct Node {
   resources: Arc<Resources>,
}

/// A blocking iterator over the RPCs this node receives.
pub struct Receptions {
   iter: bus::BusIntoIter<rpc::Rpc>,
}

impl Node {
   pub fn new(inbound_port: u16, outbound_port: u16) -> io::Result<Node> {
      let id = Hash::random();

      let resources = Arc::new(Resources {
         id         : id.clone(),
         table      : routing::Table::new(id),
         inbound    : try!(net::UdpSocket::bind(("0.0.0.0", inbound_port))),
         outbound   : try!(net::UdpSocket::bind(("0.0.0.0", outbound_port))),
         state      : sync::Mutex::new(State::Alive),
         received   : sync::Mutex::new(bus::Bus::new(UPDATE_BUS_SIZE_BYTES))
      });

      try!(resources.inbound.set_read_timeout(Some(time::Duration::new(SOCKET_TIMEOUT_S,0))));

      let weak_resources = Arc::downgrade(&resources);
      thread::spawn(move || { Node::reception_loop(weak_resources) });

      Ok( Node{ resources: resources } )
   }

   /// Produces an iterator over RPCs received by this node. The iterator will block
   /// indefinitely.
   pub fn receptions(&self) -> Receptions {
      Receptions { iter: self.resources.received.lock().unwrap().add_rx().into_iter() }
   }

   pub fn ping(&self, destination: routing::NodeInfo) {
      let resources = self.resources.clone();
      thread::spawn(move || { resources.ping(destination) });
   }

   /// Receives and processes data as long as the table is alive.
   fn reception_loop(weak: Weak<Resources>) {
      let mut buffer = [0u8; SOCKET_BUFFER_SIZE_BYTES];

      while let Some(strong) = weak.upgrade() {
         if let State::ShuttingDown = *strong.state.lock().unwrap() {
            break;
         }

         if let Ok((_, source)) = strong.inbound.recv_from(&mut buffer) {
            if let Ok(rpc) = rpc::Rpc::deserialize(&buffer) {
               thread::spawn(move || { strong.process_incoming_rpc(rpc, source) } );
            }
         }
      }
   }
}

impl Drop for Node {
   fn drop(&mut self) {
      *self.resources.state.lock().unwrap() = State::ShuttingDown;
   }
}

impl Iterator for Receptions {
   type Item = rpc::Rpc;

   fn next(&mut self) -> Option<rpc::Rpc> {
      self.iter.next()
   }
}

struct Resources {
   pub id   : Hash,
   table    : routing::Table,
   outbound : net::UdpSocket,
   inbound  : net::UdpSocket,
   state    : sync::Mutex<State>,
   received : sync::Mutex<bus::Bus<rpc::Rpc>>,
}

#[derive(Eq, PartialEq)]
enum State {
   Alive,
   Error,
   ShuttingDown,
}

impl Resources {
   pub fn ping(&self, destination: routing::NodeInfo) {
      let ping = rpc::Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let payload = ping.serialize();
      self.outbound.send_to(&payload, destination.address);
   }

   pub fn ping_response(&self, destination: routing::NodeInfo) {
      let ping_response = rpc::Rpc::ping_response(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let payload = ping_response.serialize();
      self.outbound.send_to(&payload, destination.address);
   }

   fn process_incoming_rpc(&self, rpc: rpc::Rpc, mut source: net::SocketAddr) -> serde::DeserializeResult<()> {
      source.set_port(rpc.reply_port);
      let sender = routing::NodeInfo {
         node_id : rpc.sender_id.clone(),
         address : source,
      };

      match rpc.kind {
         rpc::Kind::Ping         => self.handle_ping(rpc, sender),
         rpc::Kind::PingResponse => self.handle_ping_response(rpc, sender),
         _ => (),
      }
      Ok(())
   }

   fn handle_ping(&self, ping: rpc::Rpc, sender: routing::NodeInfo) {
      self.table.insert_node(sender.clone());
      self.ping_response(sender);
   }

   fn handle_ping_response(&self, ping: rpc::Rpc, sender: routing::NodeInfo) {
      self.table.insert_node(sender);
   }
}

#[cfg(test)]
mod tests {
   use node;
   use rpc;
   use std::thread;
   use std::time;
   use routing;
   use std::net;
   use std::str::FromStr;
   pub const POLL_FREQUENCY_MS: u64 = 50;
   pub const TRIES: u8 = 5;

   #[test]
   fn node_ping() {
      let alpha = node::Node::new(50000, 50001).unwrap();
      let beta  = node::Node::new(50002, 50003).unwrap();

      let ip = net::IpAddr::from_str("127.0.0.1").unwrap();
      let address_beta = net::SocketAddr::new(ip, 50002);

      let info_beta = routing::NodeInfo {
         node_id : beta.resources.id.clone(),
         address : address_beta,
      };

      // Before sending the ping, beta does not know about alpha.
      assert!(beta.resources.table.specific_node(&alpha.resources.id).is_none());

      // Alpha pings beta.
      alpha.ping(info_beta);

      // Eventually, beta knows of alpha.
      let mut found = beta.resources.table.specific_node(&alpha.resources.id);
      for _ in 0..TRIES {
         if found.is_some() {
            break;
         }
         found = beta.resources.table.specific_node(&alpha.resources.id);
         thread::sleep(time::Duration::from_millis(POLL_FREQUENCY_MS));
      }

      assert!(found.is_some());
   }
}
<commit_msg>Verifying ping response<commit_after>use routing;
use rpc;
use bus;

use hash::Hash;
use bincode::serde;
use std::{net, io, thread, time};
use std::sync;
use std::sync::{Weak, Arc};

pub const SOCKET_BUFFER_SIZE_BYTES : usize = 65536;
pub const SOCKET_TIMEOUT_S         : u64   = 5;
pub const UPDATE_BUS_SIZE_BYTES    : usize = 50;

/// Subotai node.
///
/// On construction, a detached thread for packet reception is
/// launched.
pub struct Node {
   resources: Arc<Resources>,
}

/// A blocking iterator over the RPCs this node receives.
pub struct Receptions {
   iter: bus::BusIntoIter<rpc::Rpc>,
}

impl Node {
   pub fn new(inbound_port: u16, outbound_port: u16) -> io::Result<Node> {
      let id = Hash::random();

      let resources = Arc::new(Resources {
         id         : id.clone(),
         table      : routing::Table::new(id),
         inbound    : try!(net::UdpSocket::bind(("0.0.0.0", inbound_port))),
         outbound   : try!(net::UdpSocket::bind(("0.0.0.0", outbound_port))),
         state      : sync::Mutex::new(State::Alive),
         received   : sync::Mutex::new(bus::Bus::new(UPDATE_BUS_SIZE_BYTES))
      });

      try!(resources.inbound.set_read_timeout(Some(time::Duration::new(SOCKET_TIMEOUT_S,0))));

      let weak_resources = Arc::downgrade(&resources);
      thread::spawn(move || { Node::reception_loop(weak_resources) });

      Ok( Node{ resources: resources } )
   }

   /// Produces an iterator over RPCs received by this node. The iterator will block
   /// indefinitely.
   pub fn receptions(&self) -> Receptions {
      Receptions { iter: self.resources.received.lock().unwrap().add_rx().into_iter() }
   }

   pub fn ping(&self, destination: routing::NodeInfo) {
      let resources = self.resources.clone();
      thread::spawn(move || { resources.ping(destination) });
   }

   /// Receives and processes data as long as the table is alive.
   fn reception_loop(weak: Weak<Resources>) {
      let mut buffer = [0u8; SOCKET_BUFFER_SIZE_BYTES];

      while let Some(strong) = weak.upgrade() {
         if let State::ShuttingDown = *strong.state.lock().unwrap() {
            break;
         }

         if let Ok((_, source)) = strong.inbound.recv_from(&mut buffer) {
            if let Ok(rpc) = rpc::Rpc::deserialize(&buffer) {
               thread::spawn(move || { strong.process_incoming_rpc(rpc, source) } );
            }
         }
      }
   }
}

impl Drop for Node {
   fn drop(&mut self) {
      *self.resources.state.lock().unwrap() = State::ShuttingDown;
   }
}

impl Iterator for Receptions {
   type Item = rpc::Rpc;

   fn next(&mut self) -> Option<rpc::Rpc> {
      self.iter.next()
   }
}

struct Resources {
   pub id   : Hash,
   table    : routing::Table,
   outbound : net::UdpSocket,
   inbound  : net::UdpSocket,
   state    : sync::Mutex<State>,
   received : sync::Mutex<bus::Bus<rpc::Rpc>>,
}

#[derive(Eq, PartialEq)]
enum State {
   Alive,
   Error,
   ShuttingDown,
}

impl Resources {
   pub fn ping(&self, destination: routing::NodeInfo) {
      let ping = rpc::Rpc::ping(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let payload = ping.serialize();
      self.outbound.send_to(&payload, destination.address);
   }

   pub fn ping_response(&self, destination: routing::NodeInfo) {
      let ping_response = rpc::Rpc::ping_response(self.id.clone(), self.inbound.local_addr().unwrap().port());
      let payload = ping_response.serialize();
      self.outbound.send_to(&payload, destination.address);
   }

   fn process_incoming_rpc(&self, rpc: rpc::Rpc, mut source: net::SocketAddr) -> serde::DeserializeResult<()> {
      source.set_port(rpc.reply_port);
      let sender = routing::NodeInfo {
         node_id : rpc.sender_id.clone(),
         address : source,
      };

      match rpc.kind {
         rpc::Kind::Ping         => self.handle_ping(rpc.clone(), sender),
         rpc::Kind::PingResponse => self.handle_ping_response(rpc.clone(), sender),
         _ => (),
      }

      self.received.lock().unwrap().broadcast(rpc);
      Ok(())
   }

   fn handle_ping(&self, ping: rpc::Rpc, sender: routing::NodeInfo) {
      self.table.insert_node(sender.clone());
      self.ping_response(sender);
   }

   fn handle_ping_response(&self, ping: rpc::Rpc, sender: routing::NodeInfo) {
      self.table.insert_node(sender);
   }
}

#[cfg(test)]
mod tests {
   use node;
   use rpc;
   use std::thread;
   use std::time;
   use routing;
   use std::net;
   use std::str::FromStr;
   pub const POLL_FREQUENCY_MS: u64 = 50;
   pub const TRIES: u8 = 5;

   #[test]
   fn node_ping() {
      let alpha = node::Node::new(50000, 50001).unwrap();
      let beta  = node::Node::new(50002, 50003).unwrap();

      let ip = net::IpAddr::from_str("127.0.0.1").unwrap();
      let address_beta = net::SocketAddr::new(ip, 50002);

      let info_beta = routing::NodeInfo {
         node_id : beta.resources.id.clone(),
         address : address_beta,
      };

      // Before sending the ping, beta does not know about alpha.
      assert!(beta.resources.table.specific_node(&alpha.resources.id).is_none());

      // Alpha pings beta.
      alpha.ping(info_beta);

      // Eventually, beta knows of alpha.
      let mut found = beta.resources.table.specific_node(&alpha.resources.id);
      for _ in 0..TRIES {
         if found.is_some() {
            break;
         }
         found = beta.resources.table.specific_node(&alpha.resources.id);
         thread::sleep(time::Duration::from_millis(POLL_FREQUENCY_MS));
      }

      assert!(found.is_some());
   }

   #[test]
   fn ping_response() {
      let alpha = node::Node::new(50004, 50005).unwrap();
      let beta  = node::Node::new(50006, 50007).unwrap();

      let ip = net::IpAddr::from_str("127.0.0.1").unwrap();
      let address_beta = net::SocketAddr::new(ip, 50006);

      let info_beta = routing::NodeInfo {
         node_id : beta.resources.id.clone(),
         address : address_beta,
      };

      alpha.ping(info_beta);
      for reply in alpha.receptions().take(1) {
         assert_eq!(rpc::Kind::PingResponse, reply.kind);
      }
   }
}
<|endoftext|>


<commit_before>use proc_macro2::{Span, TokenStream};
use syn::{Data, Field, Ident, Lit, Meta, NestedMeta, Variant};

use super::RustlerAttr;

///
/// A parsing context struct.
///
/// `Context` holds information usable for different codegen modules.
///
pub(crate) struct Context<'a> {
    pub(crate) attrs: Vec<RustlerAttr>,
    pub(crate) ident: &'a proc_macro2::Ident,
    pub(crate) ident_with_lifetime: proc_macro2::TokenStream,
    pub(crate) variants: Option<Vec<&'a Variant>>,
    pub(crate) struct_fields: Option<Vec<&'a Field>>,
}

impl<'a> Context<'a> {
    pub(crate) fn from_ast(ast: &'a syn::DeriveInput) -> Self {
        let mut attrs: Vec<_> = ast
            .attrs
            .iter()
            .map(Context::get_rustler_attrs)
            .flatten()
            .collect();

        //
        // Default: generate encoder and decoder
        //
        if !Context::encode_decode_attr_set(&attrs) {
            attrs.push(RustlerAttr::Encode);
            attrs.push(RustlerAttr::Decode);
        }

        let has_lifetime = match ast.generics.lifetimes().count() {
            0 => false,
            1 => true,
            _ => panic!("Struct can only have one lifetime argument"),
        };

        let ident = &ast.ident;
        let ident_with_lifetime = if has_lifetime {
            quote! { #ident <'a> }
        } else {
            quote! { #ident }
        };

        let variants = match ast.data {
            Data::Enum(ref data_enum) => Some(data_enum.variants.iter().collect()),
            _ => None,
        };

        let struct_fields = match ast.data {
            Data::Struct(ref data_struct) => Some(data_struct.fields.iter().collect()),
            _ => None,
        };

        Self {
            attrs,
            ident,
            ident_with_lifetime,
            variants,
            struct_fields,
        }
    }

    pub(crate) fn encode(&self) -> bool {
        self.attrs.iter().any(|attr| match attr {
            RustlerAttr::Encode => true,
            _ => false,
        })
    }

    pub(crate) fn decode(&self) -> bool {
        self.attrs.iter().any(|attr| match attr {
            RustlerAttr::Decode => true,
            _ => false,
        })
    }

    pub(crate) fn field_atoms(&self) -> Option<Vec<TokenStream>> {
        self.struct_fields.as_ref().map(|struct_fields| {
            struct_fields
                .iter()
                .map(|field| {
                    let ident = field.ident.as_ref().unwrap();
                    let ident_str = ident.to_string();

                    let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());

                    quote! {
                        #atom_fun = #ident_str,
                    }
                })
                .collect()
        })
    }

    fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool {
        attrs.iter().any(|attr| match attr {
            RustlerAttr::Encode => true,
            RustlerAttr::Decode => true,
            _ => false,
        })
    }

    fn get_rustler_attrs(attr: &syn::Attribute) -> Vec<RustlerAttr> {
        attr.path
            .segments
            .iter()
            .filter_map(|segment| {
                let meta = attr.parse_meta().expect("can parse meta");
                match segment.ident.to_string().as_ref() {
                    "rustler" => Context::parse_rustler(&meta),
                    "tag" => Context::try_parse_tag(&meta),
                    "module" => Context::try_parse_module(&meta),
                    _ => None,
                }
            })
            .flatten()
            .collect()
    }

    fn parse_rustler(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::List(ref list) = meta {
            return Some(
                list.nested
                    .iter()
                    .map(Context::parse_nested_rustler)
                    .collect(),
            );
        }

        panic!("Expected encode and/or decode in rustler attribute");
    }

    fn parse_nested_rustler(nested: &NestedMeta) -> RustlerAttr {
        if let NestedMeta::Meta(ref meta) = nested {
            if let Meta::Path(ref path) = meta {
                match path.segments[0].ident.to_string().as_ref() {
                    "encode" => return RustlerAttr::Encode,
                    "decode" => return RustlerAttr::Decode,
                    other => panic!("Unexpected literal {}", other),
                }
            }
        }

        panic!("Expected encode and/or decode in rustler attribute");
    }

    fn try_parse_tag(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::NameValue(ref name_value) = meta {
            if let Lit::Str(ref tag) = name_value.lit {
                return Some(vec![RustlerAttr::Tag(tag.value())]);
            }
        }
        panic!("Cannot parse module")
    }

    fn try_parse_module(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::NameValue(name_value) = meta {
            if let Lit::Str(ref module) = name_value.lit {
                let ident = format!("Elixir.{}", module.value());
                return Some(vec![RustlerAttr::Module(ident)]);
            }
        }
        panic!("Cannot parse tag")
    }
}
<commit_msg>codegen: Drop (crate) from pub<commit_after>use proc_macro2::{Span, TokenStream};
use syn::{Data, Field, Ident, Lit, Meta, NestedMeta, Variant};

use super::RustlerAttr;

///
/// A parsing context struct.
///
/// `Context` holds information usable for different codegen modules.
///
pub(crate) struct Context<'a> {
    pub attrs: Vec<RustlerAttr>,
    pub ident: &'a proc_macro2::Ident,
    pub ident_with_lifetime: proc_macro2::TokenStream,
    pub variants: Option<Vec<&'a Variant>>,
    pub struct_fields: Option<Vec<&'a Field>>,
}

impl<'a> Context<'a> {
    pub fn from_ast(ast: &'a syn::DeriveInput) -> Self {
        let mut attrs: Vec<_> = ast
            .attrs
            .iter()
            .map(Context::get_rustler_attrs)
            .flatten()
            .collect();

        //
        // Default: generate encoder and decoder
        //
        if !Context::encode_decode_attr_set(&attrs) {
            attrs.push(RustlerAttr::Encode);
            attrs.push(RustlerAttr::Decode);
        }

        let has_lifetime = match ast.generics.lifetimes().count() {
            0 => false,
            1 => true,
            _ => panic!("Struct can only have one lifetime argument"),
        };

        let ident = &ast.ident;
        let ident_with_lifetime = if has_lifetime {
            quote! { #ident <'a> }
        } else {
            quote! { #ident }
        };

        let variants = match ast.data {
            Data::Enum(ref data_enum) => Some(data_enum.variants.iter().collect()),
            _ => None,
        };

        let struct_fields = match ast.data {
            Data::Struct(ref data_struct) => Some(data_struct.fields.iter().collect()),
            _ => None,
        };

        Self {
            attrs,
            ident,
            ident_with_lifetime,
            variants,
            struct_fields,
        }
    }

    pub fn encode(&self) -> bool {
        self.attrs.iter().any(|attr| match attr {
            RustlerAttr::Encode => true,
            _ => false,
        })
    }

    pub fn decode(&self) -> bool {
        self.attrs.iter().any(|attr| match attr {
            RustlerAttr::Decode => true,
            _ => false,
        })
    }

    pub fn field_atoms(&self) -> Option<Vec<TokenStream>> {
        self.struct_fields.as_ref().map(|struct_fields| {
            struct_fields
                .iter()
                .map(|field| {
                    let ident = field.ident.as_ref().unwrap();
                    let ident_str = ident.to_string();

                    let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());

                    quote! {
                        #atom_fun = #ident_str,
                    }
                })
                .collect()
        })
    }

    fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool {
        attrs.iter().any(|attr| match attr {
            RustlerAttr::Encode => true,
            RustlerAttr::Decode => true,
            _ => false,
        })
    }

    fn get_rustler_attrs(attr: &syn::Attribute) -> Vec<RustlerAttr> {
        attr.path
            .segments
            .iter()
            .filter_map(|segment| {
                let meta = attr.parse_meta().expect("can parse meta");
                match segment.ident.to_string().as_ref() {
                    "rustler" => Context::parse_rustler(&meta),
                    "tag" => Context::try_parse_tag(&meta),
                    "module" => Context::try_parse_module(&meta),
                    _ => None,
                }
            })
            .flatten()
            .collect()
    }

    fn parse_rustler(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::List(ref list) = meta {
            return Some(
                list.nested
                    .iter()
                    .map(Context::parse_nested_rustler)
                    .collect(),
            );
        }

        panic!("Expected encode and/or decode in rustler attribute");
    }

    fn parse_nested_rustler(nested: &NestedMeta) -> RustlerAttr {
        if let NestedMeta::Meta(ref meta) = nested {
            if let Meta::Path(ref path) = meta {
                match path.segments[0].ident.to_string().as_ref() {
                    "encode" => return RustlerAttr::Encode,
                    "decode" => return RustlerAttr::Decode,
                    other => panic!("Unexpected literal {}", other),
                }
            }
        }

        panic!("Expected encode and/or decode in rustler attribute");
    }

    fn try_parse_tag(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::NameValue(ref name_value) = meta {
            if let Lit::Str(ref tag) = name_value.lit {
                return Some(vec![RustlerAttr::Tag(tag.value())]);
            }
        }
        panic!("Cannot parse module")
    }

    fn try_parse_module(meta: &Meta) -> Option<Vec<RustlerAttr>> {
        if let Meta::NameValue(name_value) = meta {
            if let Lit::Str(ref module) = name_value.lit {
                let ident = format!("Elixir.{}", module.value());
                return Some(vec![RustlerAttr::Module(ident)]);
            }
        }
        panic!("Cannot parse tag")
    }
}
<|endoftext|>


<commit_before>use std;

pub type Bits<T> = SubByteIter<BitParams, T>;
pub type Dibits<T> = SubByteIter<DibitParams, T>;

pub trait IterateParams {
    type IterType;

    fn bits() -> u8;
    fn wrap(x: u8) -> Self::IterType;

    fn iterations() -> u8 { 8 / Self::bits() }

    fn validate() {
        assert!(Self::bits().is_power_of_two());
    }
}

pub struct Bit(u8);

impl Bit {
    pub fn new(bits: u8) -> Bit {
        assert!(bits & 0b11111110 == 0);
        Bit(bits)
    }

    pub fn bits(&self) -> u8 { self.0 }
}

pub struct BitParams;

impl IterateParams for BitParams {
    type IterType = Bit;
    fn bits() -> u8 { 1 }
    fn wrap(x: u8) -> Bit { Bit::new(x) }
}

#[derive(Debug)]
pub struct Dibit(u8);

impl Dibit {
    pub fn new(bits: u8) -> Dibit {
        assert!(bits & 0b11111100 == 0);
        Dibit(bits)
    }

    pub fn bits(&self) -> u8 { self.0 }
}

pub struct DibitParams;

impl IterateParams for DibitParams {
    type IterType = Dibit;
    fn bits() -> u8 { 2 }
    fn wrap(x: u8) -> Dibit { Dibit::new(x) }
}

pub struct SubByteIter<P, T>
    where P: IterateParams, T: Iterator<Item = u8>
{
    params: std::marker::PhantomData<P>,
    /// The source of bits.
    src: T,
    /// The current index into the current byte.
    idx: u8,
    /// The current byte in the source.
    byte: u8,
}

impl<P, T> SubByteIter<P, T>
    where P: IterateParams, T: Iterator<Item = u8>
{
    /// Construct a new `SubByteIter<T>`.
    pub fn new(src: T) -> SubByteIter<P, T> {
        SubByteIter {
            params: std::marker::PhantomData,
            src: src,
            byte: 0,
            idx: 0,
        }
    }
}

impl<P, T> Iterator for SubByteIter<P, T>
    where P: IterateParams, T: Iterator<Item = u8>
{
    type Item = P::IterType;

    fn next(&mut self) -> Option<Self::Item> {
        if self.idx == 0 {
            self.byte = match self.src.next() {
                Some(b) => b,
                None => return None,
            };
        }

        // Store the current byte for later.
        let byte = self.byte;

        // Strip off the MSBs for the next iteration.
        self.byte <<= P::bits();

        // Move to the next item and reset after all have been visited.
        self.idx += 1;
        self.idx %= P::iterations();

        // Yield the MSBs.
        Some(P::wrap(byte >> (8 - P::bits())))
    }
}

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

    #[test]
    fn validate_params() {
        BitParams::validate();
        DibitParams::validate();
    }

    #[test]
    fn test_bits() {
        const BITS: &'static [u8] = &[
            0b00011011,
            0b11001100,
        ];

        {
            let mut d = Dibits::new(BITS.iter().cloned());

            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().unwrap().0 == 0b01);
            assert!(d.next().unwrap().0 == 0b10);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().is_none());
        }

        {
            let mut b = Bits::new(BITS.iter().cloned());

            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
        }
    }
}
<commit_msg>Add some docs to bits<commit_after>//! This module defines the `Bits` and `Dibits` iterators as well as the wrapper types
//! `Bit` and `Dibit`, for working with sub-byte values.
//!
//! The wrapped values of `Bit` and `Dibit` are guaranteed to have only one or two bits,
//! respectively.

use std;

/// Iterate over individual bits of a byte source, MSB to LSB.
pub type Bits<T> = SubByteIter<BitParams, T>;
/// Iterate over the dibits of a byte source, MSB to LSB.
pub type Dibits<T> = SubByteIter<DibitParams, T>;

/// Defines parameters needed for (power of two) sub-byte iterators.
pub trait IterParams {
    /// Type to yield at each iteration.
    type IterType;

    /// Number of bits to consume at each iteration.
    fn bits() -> u8;
    /// Wrap the given bits in container type.
    fn wrap(bits: u8) -> Self::IterType;

    /// Number of iterations needed for each byte.
    fn iterations() -> u8 { 8 / Self::bits() }

    /// Verify the parameters are supported.
    fn validate() {
        // Only powers of two are valid because there can be no "leftovers."
        assert!(Self::bits().is_power_of_two());
    }
}

/// A single bit.
pub struct Bit(u8);

impl Bit {
    /// Construct a new `Bit` with the given bit in the LSB position.
    pub fn new(bits: u8) -> Bit {
        assert!(bits & 0b11111110 == 0);
        Bit(bits)
    }

    /// Get the wrapped bit value.
    pub fn bit(&self) -> u8 { self.0 }
}

/// Parameters for `Bits` iterator.
pub struct BitParams;

impl IterParams for BitParams {
    type IterType = Bit;
    fn bits() -> u8 { 1 }
    fn wrap(bits: u8) -> Bit { Bit::new(bits) }
}

#[derive(Debug)]
/// Two bits.
pub struct Dibit(u8);

impl Dibit {
    /// Construct a new `Dibit` with the two given bits in the LSB position.
    pub fn new(bits: u8) -> Dibit {
        assert!(bits & 0b11111100 == 0);
        Dibit(bits)
    }

    /// Get the wrapped dibit.
    pub fn bits(&self) -> u8 { self.0 }
}

/// Parameters for `Dibits` iterator.
pub struct DibitParams;

impl IterParams for DibitParams {
    type IterType = Dibit;
    fn bits() -> u8 { 2 }
    fn wrap(bits: u8) -> Dibit { Dibit::new(bits) }
}

/// An iterator for sub-byte (bit-level) values.
pub struct SubByteIter<P, T> where
    P: IterParams, T: Iterator<Item = u8>
{
    params: std::marker::PhantomData<P>,
    /// Source of bytes.
    src: T,
    /// Current bit-level index into the current byte.
    idx: u8,
    /// Current byte in the source.
    byte: u8,
}

impl<P, T> SubByteIter<P, T> where
    P: IterParams, T: Iterator<Item = u8>
{
    /// Construct a new `SubByteIter` over the given byte source. All bits are iterated
    /// over, so the number of bits must be a byte multiple.
    pub fn new(src: T) -> SubByteIter<P, T> {
        SubByteIter {
            params: std::marker::PhantomData,
            src: src,
            byte: 0,
            idx: 0,
        }
    }
}

impl<P, T> Iterator for SubByteIter<P, T> where
    P: IterParams, T: Iterator<Item = u8>
{
    type Item = P::IterType;

    fn next(&mut self) -> Option<Self::Item> {
        if self.idx == 0 {
            self.byte = match self.src.next() {
                Some(b) => b,
                None => return None,
            };
        }

        // Extract MSBs.
        let bits = self.byte >> (8 - P::bits());

        // Strip off the MSBs for the next iteration.
        self.byte <<= P::bits();

        // Move to the next item and reset after all have been visited.
        self.idx += 1;
        self.idx %= P::iterations();

        Some(P::wrap(bits))
    }
}

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

    #[test]
    fn validate_params() {
        BitParams::validate();
        DibitParams::validate();
    }

    #[test]
    fn test_bits() {
        const BITS: &'static [u8] = &[
            0b00011011,
            0b11001100,
        ];

        {
            let mut d = Dibits::new(BITS.iter().cloned());

            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().unwrap().0 == 0b01);
            assert!(d.next().unwrap().0 == 0b10);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().unwrap().0 == 0b11);
            assert!(d.next().unwrap().0 == 0b00);
            assert!(d.next().is_none());
        }

        {
            let mut b = Bits::new(BITS.iter().cloned());

            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 1);
            assert!(b.next().unwrap().0 == 0);
            assert!(b.next().unwrap().0 == 0);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This crates provides an API for both structured and non-structured(text) logging.
//!
//! # Text logging
//!
//! Text logging is configured via `RUST_LOG` environment variable and has exactly same facade as the rust log crate.
//!
//! # Structured logging
//!
//! This crate contains two types of APIs for structured logging.
//!
//! 1) The `StructuredLogEntry` class and (`sl_info!`, `sl_error!`, ...) macros for directly composing structured logs.
//! 2) A bridge between traditional `log!` macro and the structured logging API (which will be deprecated).
//!
//! ## Configuration
//!
//! Structured logger has separate log levels that are configured with `STRUCT_LOG_LEVEL`.
//! It is set to `DEBUG` by default, but executes if structured logger is initialized.
//!
//! Structured logger can be initialized manually with one of the `init_XXX_struct_log()` functions.
//! The preferred way to initialize structured logging is by using `init_struct_log_from_env()`.
//! In this case, the `STRUCT_LOG_FILE` environment variable is used to set the file name for structured logs.
//!
//! ## Direct API
//!
//! ```pseudo
//! use std::collections::HashMap;
//! use serde_json::Value;
//!
//! pub struct StructuredLogEntry {
//!     log: Option<String>,
//!     pattern: Option<&'static str>,
//!     category: Option<&'static str>,
//!     name: Option<&'static str>,
//!     module: Option<&'static str>,
//!     location: Option<&'static str>,
//!     timestamp: String,
//!     level: Option<log::Level>,
//!     data: HashMap<&'static str, Value>,
//! }
//!
//! impl StructuredLogEntry {
//!     pub fn new_named(category: &'static str, name: &'static str) -> Self { /* ... */ }
//!     /* ... Builder style setters for chained initialization such as
//!         entry.data(a, b).data(x, y) ... */
//! }
//!
//! // Usage:
//! let logging_field: LoggingField<String> = LoggingField::new("String");
//! let string = "test".to_string();
//!
//! info!(StructuredLogEntry::new_named("TransactionEvents", "Committed")
//!    .data("block", &block)
//!    .data_display("author", &author)
//!    .field(&logging_field, &string)
//!    .message(format!("Committed block: {:?} by {}", block, author))
//! ```
//!
//! Arguments passed to `data()` will be serialized into JSON, and must implement `Serialize`.
//! Arguments passed to `data_display()` will instead use display and must implement `Display`.
//! Arguments passed to `field()` allows for typed fields for type checking, and must implement `Serialize`.
//! Only static strings are allowed as field names.
//!
//! (`sl_info!`, `sl_error!`, etc.) should be used to send structured log entries based on log level.
//! This macro populates the metadata such as code location and module, and skips the evaluation of
//! `StructuredLogEntry` entirely if structured logging is disabled for the log level.
//!
//! ## Typed Schema's
//!
//! The `Schema` trait can be used to implement typed logging schemas. This can either be
//! implemented by hand or derived using the `Schema` derive proc-macro, implementing the `Schema`
//! trait for the struct as well as providing setters for all fields.
//!
//! ```rust
//! use libra_logger::Schema;
//!
//! #[derive(Schema)]
//! struct LogSchema<'a> {
//!     // Log using this type's Serialize impl
//!     a: usize,
//!     // Log using this type's Debug impl
//!     #[schema(debug)]
//!     b: Option<Vec<bool>>,
//!     // Log using this type's Display impl
//!     #[schema(display)]
//!     c: Option<&'a str>,
//! }
//! ```
//!
//! ## Log macro bridge
//!
//! Crate owners are not required to rewrite their code right away to support new structured logging.
//! Importing logger crate will automatically emit structured logs on every log(`debug!`, `info!`, etc.) macro invocation.
//!
//! So
//! ```pseudo
//! info!("Committing {}", block);
//! // Will emit(in addition to regular text log) structured log such as
//! // {
//! //   level: "Info",
//! //   pattern: "Committing {}",
//! //   data: {
//! //     block: "<id>"
//! //   },
//! //   ...metadata...
//! // }
//! ```
//!
//! There are few caveats to automatic structured logging generation:
//! 1) Argument values for structured logging will be serialized using `Debug` vs `Serialize`,
//! regardless of what formatter is used for the text log. As a consequence, every log argument must
//! implement `Debug`. This has led to unexpected large logs from the default `Debug` implementations.
//!
//! 2) Field names will be automatically evaluated if the expression is a single identifier, as in
//! the example above, the field `block` will be named `block`. However, if a more complex expression
//! is passed (e.g. `block.id()`), the field name will be based on the position of the argument: `_0`, `_1`, etc.
//!
//! ```pseudo
//! info!("Committing {}", block.id());
//! //->
//! // {
//! //  data: {
//! //    "_0": "<id>"
//! //  },
//! //  ...metadata...
//! // }
//! ```
//! Another way to set the name for fields is to use named format arguments:
//! ```pseudo
//! info!("Committing {id}", id=block.id());
//! //->
//! // {
//! //  data: {
//! //    "id": "<id>"
//! //  },
//! //  ...metadata...
//! // }
//! ```
//!
//! ## Structured Log Sink
//! The application must define implementation of the StructLogSink trait in order to direct structured
//! logs emitted by `sl_info!` and other macros. The global sink can be only initialized once, by
//! calling the `set_struct_logger()` function.
//!
//! Currently 4 implementations for StructLogSink exist:
//!
//! 1) `NopStructLog` ignores structured logs
//! 2) `PrintStructLog` immediately prints structured logs to stdout
//! 3) `FileStructLog` prints structured logs into a file. This logger separate thread for writing files asynchronously.
//! 4) `TCPStructLog` sends structured logs to a TCP endpoint. This logger separate thread for sending logs asynchronously.

#![forbid(unsafe_code)]

pub mod prelude {
    pub use crate::{
        debug, error, event, info, security::SecurityEvent, trace, warn, StructuredLogEntry,
    };
}
pub mod json_log;

mod event;
mod kv;
mod libra_logger;
mod logger;
mod macros;
mod metadata;
mod security;
mod struct_log;

pub use crate::libra_logger::{
    LibraLogger, LibraLogger as Logger, LibraLoggerBuilder, Writer, CHANNEL_SIZE,
};
pub use event::Event;
pub use metadata::{Level, Metadata};

pub use struct_log::{LoggingField, StructuredLogEntry};

pub use kv::{Key, KeyValue, Schema, Value, Visitor};
pub use libra_log_derive::Schema;
pub use security::SecurityEvent;

pub mod counters;
<commit_msg>logger: update crate level documentation<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This crates provides an API for logging in libra.
//!
//! # Instrumenting with Logs
//!
//! A set of logging macros (`info!`, `error!`, `warn!`, `debug!`, and `trace!`) is provided for
//! emitting logs at different levels. All of these macros support the addition of providing
//! structured data along with a formatted text message.
//!
//! ```
//! use libra_logger::info;
//!
//! let world = "world!";
//!
//! // formatted message
//! info!("hello {}", world);
//!
//! // structured data can be logged using the format 'key = value'
//! // where value implements Serialize
//! let value1 = 5;
//! info!(key1 = value1);
//!
//! // You can even set multiple key/value pairs and a format message
//! let value2 = false;
//! info!(key1 = value1, key2 = value2, "hello {}", world);
//! ```
//!
//! ### Note
//!
//! Arguments used in a formatted message are **not** captured and included as structured data.
//!
//! ## Typed Schema's
//!
//! The `Schema` trait can be used to implement typed logging schemas. This can either be
//! implemented by hand or derived using the `Schema` derive proc-macro, implementing the `Schema`
//! trait for the struct as well as providing setters for all fields.
//!
//! ```
//! use libra_logger::{info, Schema};
//!
//! #[derive(Schema)]
//! struct LogSchema<'a> {
//!     // Log using this type's Serialize impl
//!     a: usize,
//!     // Log using this type's Debug impl
//!     #[schema(debug)]
//!     b: Option<Vec<bool>>,
//!     // Log using this type's Display impl
//!     #[schema(display)]
//!     c: Option<&'a str>,
//! }
//!
//! let log = LogSchema { a: 5, b: None, c: None };
//!
//! info!(log.c("radiant"));
//! ```
//!
//! # Configuration
//!
//! In order for logs to be captured and emitted a Logger needs to be instantiated. This can be
//! done by using the `Logger` type:
//!
//! ```
//! use libra_logger::{Level, Logger};
//!
//! Logger::builder().level(Level::Info).build();
//! ```

#![forbid(unsafe_code)]

pub mod prelude {
    pub use crate::{
        debug, error, event, info, security::SecurityEvent, trace, warn, StructuredLogEntry,
    };
}
pub mod json_log;

mod event;
mod kv;
mod libra_logger;
mod logger;
mod macros;
mod metadata;
mod security;
mod struct_log;

pub use crate::libra_logger::{
    LibraLogger, LibraLogger as Logger, LibraLoggerBuilder, Writer, CHANNEL_SIZE,
};
pub use event::Event;
pub use metadata::{Level, Metadata};

pub use struct_log::{LoggingField, StructuredLogEntry};

pub use kv::{Key, KeyValue, Schema, Value, Visitor};
pub use libra_log_derive::Schema;
pub use security::SecurityEvent;

pub mod counters;
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

register_long_diagnostics! {

}

register_diagnostics! {
    E0510, // invalid use of `return_address` intrinsic: function does not use out pointer
    E0511, // invalid monomorphization of `{}` intrinsic
    E0512, // transmute called on types with potentially different sizes...
}
<commit_msg>Add error explanation for E0515<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_snake_case)]

register_long_diagnostics! {

E0515: r##"
A constant index expression was out of bounds. Erroneous code example:

```
let x = &[0, 1, 2][7]; // error: const index-expr is out of bounds
```

Please specify a valid index (not inferior to 0 or superior to array length).
Example:

```
let x = &[0, 1, 2][2]; // ok!
```
"##,

}

register_diagnostics! {
    E0510, // invalid use of `return_address` intrinsic: function does not use out pointer
    E0511, // invalid monomorphization of `{}` intrinsic
    E0512, // transmute called on types with potentially different sizes...
}
<|endoftext|>


<commit_before>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::rc::Rc;

/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
    Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
}

impl Frame {
    /// Construct a new frame around the delimited set of tokens.
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription.
/// - `interp` is a map of meta-variables to the tokens (non-terminals) they matched in the
///   invocation. We are assuming we already know there is a match.
/// - `src` is the RHS of the MBE, that is, the "example" we are filling in.
///
/// For example,
///
/// ```rust
/// macro_rules! foo {
///     ($id:ident) => { println!("{}", stringify!($id)); }
/// }
///
/// foo!(bar);
/// ```
///
/// `interp` would contain `$id => bar` and `src` would contain `println!("{}", stringify!($id));`.
///
/// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`.
///
/// Along the way, we do some additional error checking.
pub fn transcribe(
    cx: &ExtCtxt<'_>,
    interp: &FxHashMap<Ident, Rc<NamedMatch>>,
    src: Vec<quoted::TokenTree>,
) -> TokenStream {
    // Nothing for us to transcribe...
    if src.is_empty() {
        return TokenStream::empty();
    }

    // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
    // we have yet to expand/are still expanding. We start the stack off with the whole RHS.
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];

    // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
    // `repeats` keeps track of where we are in matching at each level, with the last element being
    // the most deeply nested sequence. This is used as a stack.
    let mut repeats = Vec::new();

    // `result` contains resulting token stream from the TokenTree we just finished processing. At
    // the end, this will contain the full result of transcription, but at arbitrary points during
    // `transcribe`, `result` will contain subsets of the final result.
    //
    // Specifically, as we descend into each TokenTree, we will push the existing results onto the
    // `result_stack` and clear `results`. We will then produce the results of transcribing the
    // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
    // `result_stack` and append `results` too it to produce the new `results` up to that point.
    //
    // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
    // again, and we are done transcribing.
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        // Look at the last frame on the stack.
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            // If it still has a TokenTree we have not looked at yet, use that tree.
            tree
        }
        // The else-case never produces a value for `tree` (it `continue`s or `return`s).
        else {
            // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
            // go back to the beginning of the sequence.
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue;
                }
            }

            // We are done with the top of the stack. Pop it. Depending on what it was, we do
            // different things. Note that the outermost item must be the delimited, wrapped RHS
            // that was passed in originally to `transcribe`.
            match stack.pop().unwrap() {
                // Done with a sequence. Pop from repeats.
                Frame::Sequence { .. } => {
                    repeats.pop();
                }

                // We are done processing a Delimited. If this is the top-level delimited, we are
                // done. Otherwise, we unwind the result_stack to append what we have produced to
                // any previous results.
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        // No results left to compute! We are back at the top-level.
                        return TokenStream::new(result);
                    }

                    // Step back into the parent Delimited.
                    let tree =
                        TokenTree::Delimited(span, forest.delim, TokenStream::new(result).into());
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue;
        };

        // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
        // `tree` contains the next `TokenTree` to be processed.
        match tree {
            // We are descending into a sequence. We first make sure that the matchers in the RHS
            // and the matches in `interp` have the same shape. Otherwise, either the caller or the
            // macro writer has made a mistake.
            seq @ quoted::TokenTree::Sequence(..) => {
                match lockstep_iter_size(&seq, interp, &repeats) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(
                            seq.span(), /* blame macro writer */
                            "attempted to repeat an expression containing no syntax variables \
                             matched as repeating at this depth",
                        );
                    }

                    LockstepIterSize::Contradiction(ref msg) => {
                        // This should never happen because the macro parser should generate
                        // properly-sized matches for all meta-vars.
                        cx.span_bug(seq.span(), &msg[..]);
                    }

                    LockstepIterSize::Constraint(len, _) => {
                        // We do this to avoid an extra clone above. We know that this is a
                        // sequence already.
                        let (sp, seq) = if let quoted::TokenTree::Sequence(sp, seq) = seq {
                            (sp, seq)
                        } else {
                            unreachable!()
                        };

                        // Is the repetition empty?
                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // This should be impossible because the macro parser would not
                                // match the given macro arm.
                                cx.span_bug(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            // 0 is the initial counter (we have done 0 repretitions so far). `len`
                            // is the total number of reptitions we should generate.
                            repeats.push((0, len));

                            // The first time we encounter the sequence we push it to the stack. It
                            // then gets reused (see the beginning of the loop) until we are done
                            // repeating.
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }

            // Replace the meta-var with the matched token tree from the invocation.
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                // Find the matched nonterminal from the macro invocation, and use it to replace
                // the meta-var.
                if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        // FIXME #2887: why do we apply a mark when matching a token tree meta-var
                        // (e.g. `$x:tt`), but not when we are matching any other type of token
                        // tree?
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        // We were unable to descend far enough. This is an error.
                        cx.span_fatal(
                            sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident),
                        );
                    }
                } else {
                    // If we aren't able to match the meta-var, we push it back into the result but
                    // with modified syntax context. (I believe this supports nested macros).
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }

            // If we are entering a new delimiter, we push its contents to the `stack` to be
            // processed, and we push all of the currently produced results to the `result_stack`.
            // We will produce all of the results of the inside of the `Delimited` and then we will
            // jump back out of the Delimited, pop the result_stack and add the new results back to
            // the previous results (from outside the Delimited).
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }

            // Nothing much to do here. Just push the token to the result, being careful to
            // preserve syntax context.
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }

            // There should be no meta-var declarations in the invocation of a macro.
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
/// the set of matches `interpolations`.
///
/// See the definition of `repeats` in the `transcribe` function. `repeats` is used to descend
/// into the right place in nested matchers. If we attempt to descend too far, the macro writer has
/// made a mistake, and we return `None`.
fn lookup_cur_matched(
    ident: Ident,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

/// An accumulator over a TokenTree to be used with `fold`. During transcription, we need to make
/// sure that the size of each sequence and all of its nested sequences are the same as the sizes
/// of all the matched (nested) sequences in the macro invocation. If they don't match, somebody
/// has made a mistake (either the macro writer or caller).
#[derive(Clone)]
enum LockstepIterSize {
    /// No constraints on length of matcher. This is true for any TokenTree variants except a
    /// `MetaVar` with an actual `MatchedSeq` (as opposed to a `MatchedNonterminal`).
    Unconstrained,

    /// A `MetaVar` with an actual `MatchedSeq`. The length of the match and the name of the
    /// meta-var are returned.
    Constraint(usize, Ident),

    /// Two `Constraint`s on the same sequence had different lengths. This is an error.
    Contradiction(String),
}

impl LockstepIterSize {
    /// Find incompatibilities in matcher/invocation sizes.
    /// - `Unconstrained` is compatible with everything.
    /// - `Contradiction` is incompatible with everything.
    /// - `Constraint(len)` is only compatible with other constraints of the same length.
    fn with(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!(
                        "inconsistent lockstep iteration: \
                         '{}' has {} items, but '{}' has {}",
                        l_id, l_len, r_id, r_len
                    );
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

/// Given a `tree`, make sure that all sequences have the same length as the matches for the
/// appropriate meta-vars in `interpolations`.
///
/// Note that if `repeats` does not match the exact correct depth of a meta-var,
/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
/// multiple nested matcher sequences.
fn lockstep_iter_size(
    tree: &quoted::TokenTree,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size.with(lockstep_iter_size(tt, interpolations, repeats))
            })
        }
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size.with(lockstep_iter_size(tt, interpolations, repeats))
            })
        }
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained,
            }
        }
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<commit_msg>these errors can happen after all<commit_after>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::rc::Rc;

/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
    Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
}

impl Frame {
    /// Construct a new frame around the delimited set of tokens.
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription.
/// - `interp` is a map of meta-variables to the tokens (non-terminals) they matched in the
///   invocation. We are assuming we already know there is a match.
/// - `src` is the RHS of the MBE, that is, the "example" we are filling in.
///
/// For example,
///
/// ```rust
/// macro_rules! foo {
///     ($id:ident) => { println!("{}", stringify!($id)); }
/// }
///
/// foo!(bar);
/// ```
///
/// `interp` would contain `$id => bar` and `src` would contain `println!("{}", stringify!($id));`.
///
/// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`.
///
/// Along the way, we do some additional error checking.
pub fn transcribe(
    cx: &ExtCtxt<'_>,
    interp: &FxHashMap<Ident, Rc<NamedMatch>>,
    src: Vec<quoted::TokenTree>,
) -> TokenStream {
    // Nothing for us to transcribe...
    if src.is_empty() {
        return TokenStream::empty();
    }

    // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
    // we have yet to expand/are still expanding. We start the stack off with the whole RHS.
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];

    // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
    // `repeats` keeps track of where we are in matching at each level, with the last element being
    // the most deeply nested sequence. This is used as a stack.
    let mut repeats = Vec::new();

    // `result` contains resulting token stream from the TokenTree we just finished processing. At
    // the end, this will contain the full result of transcription, but at arbitrary points during
    // `transcribe`, `result` will contain subsets of the final result.
    //
    // Specifically, as we descend into each TokenTree, we will push the existing results onto the
    // `result_stack` and clear `results`. We will then produce the results of transcribing the
    // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
    // `result_stack` and append `results` too it to produce the new `results` up to that point.
    //
    // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
    // again, and we are done transcribing.
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        // Look at the last frame on the stack.
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            // If it still has a TokenTree we have not looked at yet, use that tree.
            tree
        }
        // The else-case never produces a value for `tree` (it `continue`s or `return`s).
        else {
            // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
            // go back to the beginning of the sequence.
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue;
                }
            }

            // We are done with the top of the stack. Pop it. Depending on what it was, we do
            // different things. Note that the outermost item must be the delimited, wrapped RHS
            // that was passed in originally to `transcribe`.
            match stack.pop().unwrap() {
                // Done with a sequence. Pop from repeats.
                Frame::Sequence { .. } => {
                    repeats.pop();
                }

                // We are done processing a Delimited. If this is the top-level delimited, we are
                // done. Otherwise, we unwind the result_stack to append what we have produced to
                // any previous results.
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        // No results left to compute! We are back at the top-level.
                        return TokenStream::new(result);
                    }

                    // Step back into the parent Delimited.
                    let tree =
                        TokenTree::Delimited(span, forest.delim, TokenStream::new(result).into());
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue;
        };

        // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
        // `tree` contains the next `TokenTree` to be processed.
        match tree {
            // We are descending into a sequence. We first make sure that the matchers in the RHS
            // and the matches in `interp` have the same shape. Otherwise, either the caller or the
            // macro writer has made a mistake.
            seq @ quoted::TokenTree::Sequence(..) => {
                match lockstep_iter_size(&seq, interp, &repeats) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(
                            seq.span(), /* blame macro writer */
                            "attempted to repeat an expression containing no syntax variables \
                             matched as repeating at this depth",
                        );
                    }

                    LockstepIterSize::Contradiction(ref msg) => {
                        // FIXME: this really ought to be caught at macro definition time... It
                        // happens when two meta-variables are used in the same repetition in a
                        // sequence, but they come from different sequence matchers and repeat
                        // different amounts.
                        cx.span_fatal(seq.span(), &msg[..]);
                    }

                    LockstepIterSize::Constraint(len, _) => {
                        // We do this to avoid an extra clone above. We know that this is a
                        // sequence already.
                        let (sp, seq) = if let quoted::TokenTree::Sequence(sp, seq) = seq {
                            (sp, seq)
                        } else {
                            unreachable!()
                        };

                        // Is the repetition empty?
                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // FIXME: this really ought to be caught at macro definition
                                // time... It happens when the Kleene operator in the matcher and
                                // the body for the same meta-variable do not match.
                                cx.span_fatal(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            // 0 is the initial counter (we have done 0 repretitions so far). `len`
                            // is the total number of reptitions we should generate.
                            repeats.push((0, len));

                            // The first time we encounter the sequence we push it to the stack. It
                            // then gets reused (see the beginning of the loop) until we are done
                            // repeating.
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }

            // Replace the meta-var with the matched token tree from the invocation.
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                // Find the matched nonterminal from the macro invocation, and use it to replace
                // the meta-var.
                if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        // FIXME #2887: why do we apply a mark when matching a token tree meta-var
                        // (e.g. `$x:tt`), but not when we are matching any other type of token
                        // tree?
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        // We were unable to descend far enough. This is an error.
                        cx.span_fatal(
                            sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident),
                        );
                    }
                } else {
                    // If we aren't able to match the meta-var, we push it back into the result but
                    // with modified syntax context. (I believe this supports nested macros).
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }

            // If we are entering a new delimiter, we push its contents to the `stack` to be
            // processed, and we push all of the currently produced results to the `result_stack`.
            // We will produce all of the results of the inside of the `Delimited` and then we will
            // jump back out of the Delimited, pop the result_stack and add the new results back to
            // the previous results (from outside the Delimited).
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }

            // Nothing much to do here. Just push the token to the result, being careful to
            // preserve syntax context.
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }

            // There should be no meta-var declarations in the invocation of a macro.
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
/// the set of matches `interpolations`.
///
/// See the definition of `repeats` in the `transcribe` function. `repeats` is used to descend
/// into the right place in nested matchers. If we attempt to descend too far, the macro writer has
/// made a mistake, and we return `None`.
fn lookup_cur_matched(
    ident: Ident,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

/// An accumulator over a TokenTree to be used with `fold`. During transcription, we need to make
/// sure that the size of each sequence and all of its nested sequences are the same as the sizes
/// of all the matched (nested) sequences in the macro invocation. If they don't match, somebody
/// has made a mistake (either the macro writer or caller).
#[derive(Clone)]
enum LockstepIterSize {
    /// No constraints on length of matcher. This is true for any TokenTree variants except a
    /// `MetaVar` with an actual `MatchedSeq` (as opposed to a `MatchedNonterminal`).
    Unconstrained,

    /// A `MetaVar` with an actual `MatchedSeq`. The length of the match and the name of the
    /// meta-var are returned.
    Constraint(usize, Ident),

    /// Two `Constraint`s on the same sequence had different lengths. This is an error.
    Contradiction(String),
}

impl LockstepIterSize {
    /// Find incompatibilities in matcher/invocation sizes.
    /// - `Unconstrained` is compatible with everything.
    /// - `Contradiction` is incompatible with everything.
    /// - `Constraint(len)` is only compatible with other constraints of the same length.
    fn with(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!(
                        "meta-variable `{}` repeats {} times, but `{}` repeats {} times",
                        l_id, l_len, r_id, r_len
                    );
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

/// Given a `tree`, make sure that all sequences have the same length as the matches for the
/// appropriate meta-vars in `interpolations`.
///
/// Note that if `repeats` does not match the exact correct depth of a meta-var,
/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of
/// multiple nested matcher sequences.
fn lockstep_iter_size(
    tree: &quoted::TokenTree,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size.with(lockstep_iter_size(tt, interpolations, repeats))
            })
        }
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size.with(lockstep_iter_size(tt, interpolations, repeats))
            })
        }
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained,
            }
        }
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<|endoftext|>


<commit_before>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CompareCliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to execute the `compare`
// subcommand.
struct CompareData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

enum SubcommandData {
    Compare(CompareData),
    Checksum(std::path::PathBuf),
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn compare_data_from(matches: &ArgMatches) -> Result<SubcommandData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CompareCliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(SubcommandData::Compare(CompareData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    }))
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<SubcommandData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            App::new("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("compare") {
        return compare_data_from(&matches);
    }
    Err(ZakopaneError::Unknown("not implemented".to_string()))
}

fn do_compare(data: CompareData) {
    let CompareData {
        config,
        new_snapshot,
        old_snapshot,
    } = data;
    assert!(config.rules() > 0);
    let violations = libzakopane::compare(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}

fn main() {
    let subcommand = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };
    match subcommand {
        SubcommandData::Compare(compare_data) => return do_compare(compare_data),
        _ => eprintln!("not implemented"),
    }
}
<commit_msg>checksum: Add clap handling<commit_after>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CompareCliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to execute the `compare`
// subcommand.
struct CompareData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

enum SubcommandData {
    Compare(CompareData),
    Checksum(std::path::PathBuf),
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn compare_data_from(matches: &ArgMatches) -> Result<SubcommandData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CompareCliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(SubcommandData::Compare(CompareData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    }))
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<SubcommandData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            App::new("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .subcommand(
            App::new("checksum")
                .about("produces checksums for a directory")
                .arg(
                    Arg::with_name("target-path")
                        .help("directory to checksum")
                        .index(1)
                        .required(true),
                ),
        )
        .get_matches();

    if let Some(ref matches) = matches.subcommand_matches("compare") {
        return compare_data_from(&matches);
    }
    if let Some(ref matches) = matches.subcommand_matches("checksum") {
        return Ok(SubcommandData::Checksum(std::path::PathBuf::from(
            matches.value_of("target-path").unwrap(),
        )));
    }
    Err(ZakopaneError::Unknown("not implemented".to_string()))
}

fn do_compare(data: CompareData) {
    let CompareData {
        config,
        new_snapshot,
        old_snapshot,
    } = data;
    assert!(config.rules() > 0);
    let violations = libzakopane::compare(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}

fn do_checksum(path: std::path::PathBuf) {
    eprintln!("not implemented: checksum {}", path.display());
}

fn main() {
    let subcommand = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };
    match subcommand {
        SubcommandData::Compare(compare_data) => return do_compare(compare_data),
        SubcommandData::Checksum(target_path) => return do_checksum(target_path),
    }
}
<|endoftext|>