<commit_before>use std::fs::File;
use std::io::{BufReader, BufRead};
use std::path::Path;
use std::collections::HashMap;
use super::error::*;
use super::TaskId;

#[derive(Debug)]
pub struct ProcStatus {
  pub pid: TaskId,
  pub ppid: TaskId,
  pub tgid: TaskId,
  pub name: String,
  // uid: Real, Effective, Saved, Filesystem
  pub uid: (u32, u32, u32, u32),
  // gid: Real, Effective, Saved, Filesystem
  pub gid: (u32, u32, u32, u32)
}

macro_rules! extract_key {
  ($map:expr, $key:expr, $func:expr) =>
    (try!(
      $map.remove($key)
        .and_then(|o|
          $func(o)
        )
        .ok_or(ProcHardError(ProcParseError, ProcPartStatus))
    ))
}

impl ProcStatus {
  // Generate ProcStatus struct given a process directory
  pub fn new(proc_dir: &str) -> Result<Self, ProcError> {
    // Try opening file
    let status_file = try!(
      File::open(Path::new(proc_dir).join("status"))
        .or(Err(ProcSoftError(ProcReadError, ProcPartStatus)))
    );

    let mut status: HashMap<String, String> =
      BufReader::new(status_file).lines().filter_map(
        |line|
          line
            .or(Err(ProcSoftError(ProcReadError, ProcPartStatus)))
            .and_then(|line| {
              let split = line.splitn(2, ':').collect::<Vec<&str>>();

              match (split.get(0), split.get(1)) {
                (Some(key), Some(value)) =>
                  Ok((key.trim().to_owned(),
                     value.trim().to_owned())),
                _ => Err(ProcHardError(ProcParseError, ProcPartStatus))
              }
             }).ok()
      ).collect();

    Ok(ProcStatus{
      pid: extract_key!(status, "Pid", parse_taskid),
      ppid: extract_key!(status, "PPid", parse_taskid),
      tgid: extract_key!(status, "Tgid", parse_taskid),
      name: extract_key!(status, "Name", |a| Some(a)),
      uid : extract_key!(status, "Uid", parse_uids),
      gid : extract_key!(status, "Gid", parse_uids)
    })
  }
}

fn parse_uids(uid_str: String) -> Option<(u32, u32, u32, u32)> {
  let uids = match
    uid_str.split("\t")
      .filter(|s| s != &"")
      .map(|s|
        s.parse()
      ).collect::<Result<Vec<_>, _>>()
      .ok()
    {
      Some(s) => s,
      None => return None
    };
  if uids.len() != 4 {
    return None;
  }
  Some((uids[0], uids[1], uids[2], uids[3]))
}

fn parse_taskid(taskid_str: String) -> Option<TaskId> {
  taskid_str.parse().ok()
}
<commit_msg>Implement (hopefully) faster parsing for status file<commit_after>use std::fs::File;
use std::io::{BufReader, BufRead};
use std::path::Path;
use super::error::*;
use super::TaskId;

#[derive(Debug)]
pub struct ProcStatus {
  pub pid: TaskId,
  pub ppid: TaskId,
  pub tgid: TaskId,
  pub name: String,
  // uid: Real, Effective, Saved, Filesystem
  pub uid: (u32, u32, u32, u32),
  // gid: Real, Effective, Saved, Filesystem
  pub gid: (u32, u32, u32, u32)
}

macro_rules! extract_line {
  ($iter:expr, $key:expr, $func:expr) =>
    (try!(
      match $iter.find(|r| {
          match *r {
            Ok((ref k, _)) if k == $key => true,
            Err(_) => true,
            _ => false
          }
      }) {
        Some(Ok((_, v))) => Ok($func(v)),
        Some(Err(e)) => Err(e),
        None => Err(ProcHardError(ProcParseError, ProcPartStatus))
      }
    ).unwrap())
}

impl ProcStatus {
  // Generate ProcStatus struct given a process directory
  pub fn new(proc_dir: &str) -> Result<Self, ProcError> {
    // Try opening file
    let status_file = try!(
      File::open(Path::new(proc_dir).join("status"))
        .or(Err(ProcSoftError(ProcReadError, ProcPartStatus)))
    );

    let lines =
      BufReader::new(status_file)
        .lines()
        .map(|r|
          match r {
            Ok(o) => Ok(o),
            Err(_) => Err(ProcSoftError(ProcReadError, ProcPartStatus))
          }
        );
    Self::parse_string(lines)
  }

  fn parse_string<I: Iterator<Item=Result<String, ProcError>>>(lines: I) -> Result<Self, ProcError> {
    let mut status = lines
      .map(|r|
        match r {
          Ok(line) => {
            let split = line.splitn(2, ':').collect::<Vec<&str>>();
            match (split.get(0), split.get(1)) {
              (Some(key), Some(value)) =>
                Ok((key.trim().to_owned(),
                   value.trim().to_owned())),
              _ => Err(ProcHardError(ProcParseError, ProcPartStatus))
            }
          },
          Err(_) => Err(ProcSoftError(ProcReadError, ProcPartStatus))
        }
      );

    // It's quite important that these appear in the order that they
    // appear in the status file
    Ok(ProcStatus{
      name: extract_line!(status, "Name", |a| Some(a)),
      tgid: extract_line!(status, "Tgid", parse_taskid),
      pid: extract_line!(status, "Pid", parse_taskid),
      ppid: extract_line!(status, "PPid", parse_taskid),
      uid : extract_line!(status, "Uid", parse_uids),
      gid : extract_line!(status, "Gid", parse_uids)
    })
  }
}

fn parse_uids(uid_str: String) -> Option<(u32, u32, u32, u32)> {
  let uids = match
    uid_str.split("\t")
      .filter(|s| s != &"")
      .map(|s|
        s.parse()
      ).collect::<Result<Vec<_>, _>>()
      .ok()
    {
      Some(s) => s,
      None => return None
    };
  if uids.len() != 4 {
    return None;
  }
  Some((uids[0], uids[1], uids[2], uids[3]))
}

fn parse_taskid(taskid_str: String) -> Option<TaskId> {
  taskid_str.parse().ok()
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Internal macros that provide a "template" for a protocol struct definition,
//! to help cut down on boilerplate.

macro_rules! protocol_struct {
    (
        $(#[$cmd_meta:meta])*
        type $Command:ident;
        $(type Error = $Error:ty;)?

        const TYPE: $CommandType:ty = $TYPE:ident;

        $(#[$req_meta:meta])*
        $(#[@static($sreq_meta:meta)])*
        $req_kw:ident Request $(<$req_lt:lifetime>)? {
            $($req_fields:tt)*
        }

        fn Request::from_wire($r_req:tt, $a_req:tt) $req_from:block

        fn Request::to_wire(&$self_req:tt, $w_req:tt) $req_to:block

        $(
            $(#[$rsp_meta:meta])*
            $(#[@static($srsp_meta:meta)])*
            $rsp_kw:ident Response $(<$rsp_lt:lifetime>)? {
                $($rsp_fields:tt)*
            }

            fn Response::from_wire($r_rsp:tt, $a_rsp:tt) $rsp_from:block

            fn Response::to_wire(&$self_rsp:tt, $w_rsp:tt) $rsp_to:block
        )*
    ) => {paste::paste!{
        #[allow(unused_imports)]
        mod generated {
            use super::*;
            use $crate::io::ReadZero;
            use $crate::io::Write;
            use $crate::mem::Arena;
            use $crate::protocol::wire;
            use $crate::protocol::wire::FromWire;
            use $crate::protocol::wire::ToWire;
            use $crate::protocol::Command;
            use $crate::protocol::error::NoSpecificError;
            use $crate::protocol::Message;

            #[cfg(feature = "arbitrary-derive")]
            use libfuzzer_sys::arbitrary::{self, Arbitrary};

            $(#[$cmd_meta])*
            #[doc = "Corresponds to [`" $CommandType "::" $TYPE "`]."]
            pub enum $Command {}

            impl<'wire> Command<'wire> for $Command {
                type CommandType = $CommandType;
                type Req = Req<'wire>;
                type Resp = Resp<'wire>;
                type Error = protocol_struct!(@internal if_nonempty ($($Error)?) {
                    $($Error)?
                } else {
                    NoSpecificError
                });
            }

            protocol_struct!(@internal if_nonempty ($($req_lt)?) {
                derive_borrowed! {
                    #[doc = "The [`" $Command "`] request."]
                    #[doc = ""]
                    #[doc = "Prefer to refer to this type as `Req<" $Command ">`."]
                    #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
                    $(#[$req_meta])*
                    #[@static(
                        derive(Clone, PartialEq, Eq, Debug),
                        cfg_attr(feature = "serde", derive(serde::Deserialize)),
                        cfg_attr(feature = "arbitrary-derive", derive(Arbitrary)),
                    )]
                    $(#[@static($sreq_meta)])*
                    pub $req_kw [<$Command Request>] $(<$req_lt>)? {
                        $($req_fields)*
                    }
                }
                type Req<'wire> = [<$Command Request>]<'wire>;
            } else {
                derive_borrowed! {
                    #[doc = "The [`" $Command "`] request."]
                    #[doc = ""]
                    #[doc = "Prefer to refer to this type as `Req<" $Command ">`."]
                    #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
                    #[cfg_attr(feature = "arbitrary-derive", derive(Arbitrary))]
                    $(#[$req_meta])*
                    $(#[@static($sreq_meta)])*
                    pub $req_kw [<$Command Request>] {
                        $($req_fields)*
                    }
                }
                type Req<'wire> = [<$Command Request>];
            });

            impl<'wire> Message<'wire> for Req<'wire> {
                type CommandType = $CommandType;
                const TYPE: $CommandType = $CommandType::$TYPE;
            }

            impl<'wire> FromWire<'wire> for Req<'wire> {
                fn from_wire<R: ReadZero<'wire> + ?Sized>(
                    $r_req: &mut R,
                    $a_req: &'wire dyn Arena,
                ) -> Result<Self, wire::Error> {
                    $req_from
                }
            }

            impl ToWire for Req<'_> {
                fn to_wire<W: Write>(&$self_req, mut $w_req: W) -> Result<(), wire::Error> {
                    $req_to
                }
            }

            $(
                protocol_struct!(@internal if_nonempty ($($rsp_lt)?) {
                    derive_borrowed! {
                        #[doc = "The [`" $Command "`] response."]
                        #[doc = ""]
                        #[doc = "Prefer to refer to this type as `Resp<" $Command ">`."]
                        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                        #[cfg_attr(feature = "serde", derive(serde::Serialize))]
                        $(#[$rsp_meta])*
                        #[@static(
                            derive(Clone, PartialEq, Eq, Debug),
                            cfg_attr(feature = "serde", derive(serde::Deserialize)),
                            cfg_attr(feature = "arbitrary-derive", derive(Arbitrary)),
                        )]
                        $(#[@static($srsp_meta)])*
                        pub $rsp_kw [<$Command Response>] $(<$rsp_lt>)? {
                            $($rsp_fields)*
                        }
                    }
                    type Resp<'wire> = [<$Command Response>]<'wire>;
                } else {
                    derive_borrowed! {
                        #[doc = "The [`" $Command "`] response."]
                        #[doc = ""]
                        #[doc = "Prefer to refer to this type as `Resp<" $Command ">`."]
                        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
                        #[cfg_attr(feature = "arbitrary-derive", derive(Arbitrary))]
                        $(#[$rsp_meta])*
                        $(#[@static($srsp_meta)])*
                        pub $rsp_kw [<$Command Response>] {
                            $($rsp_fields)*
                        }
                    }
                    type Resp<'wire> = [<$Command Response>];
                });

                impl<'wire> Message<'wire> for Resp<'wire> {
                    type CommandType = $CommandType;
                    const TYPE: $CommandType = $CommandType::$TYPE;
                }

                impl<'wire> FromWire<'wire> for Resp<'wire> {
                    fn from_wire<R: ReadZero<'wire> + ?Sized>(
                        $r_rsp: &mut R,
                        $a_rsp: &'wire dyn Arena,
                    ) -> Result<Self, wire::Error> {
                        $rsp_from
                    }
                }

                impl ToWire for Resp<'_> {
                    fn to_wire<W: Write>(&$self_rsp, mut $w_rsp: W) -> Result<(), wire::Error> {
                        $rsp_to
                    }
                }
            )*
        }
        pub use generated::*;
    }};

    (@internal if_nonempty () { $($t:tt)* } else { $($f:tt)* }) => {$($f)*};
    (@internal if_nonempty ($($nonempty:tt)+) { $($t:tt)* } else { $($f:tt)* }) => {$($t)*};
}
<commit_msg>[protocol] Add a way to disable `derive(Arbitrary)` as needed<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Internal macros that provide a "template" for a protocol struct definition,
//! to help cut down on boilerplate.

macro_rules! protocol_struct {
    (
        $(#[$cmd_meta:meta])*
        type $Command:ident;
        $(type Error = $Error:ty;)?

        const TYPE: $CommandType:ty = $TYPE:ident;

        $(#![fuzz_derives_if = $($req_fuzz_condition:tt)*])?
        $(#[$req_meta:meta])*
        $(#[@static($sreq_meta:meta)])*
        $req_kw:ident Request $(<$req_lt:lifetime>)? {
            $($req_fields:tt)*
        }

        fn Request::from_wire($r_req:tt, $a_req:tt) $req_from:block

        fn Request::to_wire(&$self_req:tt, $w_req:tt) $req_to:block

        $(
            $(#![fuzz_derives_if = $($rsp_fuzz_condition:tt)*])?
            $(#[$rsp_meta:meta])*
            $(#[@static($srsp_meta:meta)])*
            $rsp_kw:ident Response $(<$rsp_lt:lifetime>)? {
                $($rsp_fields:tt)*
            }

            fn Response::from_wire($r_rsp:tt, $a_rsp:tt) $rsp_from:block

            fn Response::to_wire(&$self_rsp:tt, $w_rsp:tt) $rsp_to:block
        )*
    ) => {paste::paste!{
        #[allow(unused_imports)]
        mod generated {
            use super::*;
            use $crate::io::ReadZero;
            use $crate::io::Write;
            use $crate::mem::Arena;
            use $crate::protocol::wire;
            use $crate::protocol::wire::FromWire;
            use $crate::protocol::wire::ToWire;
            use $crate::protocol::Command;
            use $crate::protocol::error::NoSpecificError;
            use $crate::protocol::Message;

            #[cfg(feature = "arbitrary-derive")]
            use libfuzzer_sys::arbitrary::{self, Arbitrary};

            $(#[$cmd_meta])*
            #[doc = "Corresponds to [`" $CommandType "::" $TYPE "`]."]
            pub enum $Command {}

            impl<'wire> Command<'wire> for $Command {
                type CommandType = $CommandType;
                type Req = Req<'wire>;
                type Resp = Resp<'wire>;
                type Error = protocol_struct!(@internal if_nonempty ($($Error)?) {
                    $($Error)?
                } else {
                    NoSpecificError
                });
            }

            protocol_struct!(@internal if_nonempty ($($req_lt)?) {
                derive_borrowed! {
                    #[doc = "The [`" $Command "`] request."]
                    #[doc = ""]
                    #[doc = "Prefer to refer to this type as `Req<" $Command ">`."]
                    #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
                    $(#[$req_meta])*
                    #[@static(
                        derive(Clone, PartialEq, Eq, Debug),
                        cfg_attr(feature = "serde", derive(serde::Deserialize)),
                        cfg_attr(all($($($req_fuzz_condition)*,)? feature = "arbitrary-derive"), derive(Arbitrary)),
                    )]
                    $(#[@static($sreq_meta)])*
                    pub $req_kw [<$Command Request>] $(<$req_lt>)? {
                        $($req_fields)*
                    }
                }
                type Req<'wire> = [<$Command Request>]<'wire>;
            } else {
                derive_borrowed! {
                    #[doc = "The [`" $Command "`] request."]
                    #[doc = ""]
                    #[doc = "Prefer to refer to this type as `Req<" $Command ">`."]
                    #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
                    #[cfg_attr(all($($($req_fuzz_condition)*,)? feature = "arbitrary-derive"), derive(Arbitrary))]
                    $(#[$req_meta])*
                    $(#[@static($sreq_meta)])*
                    pub $req_kw [<$Command Request>] {
                        $($req_fields)*
                    }
                }
                type Req<'wire> = [<$Command Request>];
            });

            impl<'wire> Message<'wire> for Req<'wire> {
                type CommandType = $CommandType;
                const TYPE: $CommandType = $CommandType::$TYPE;
            }

            impl<'wire> FromWire<'wire> for Req<'wire> {
                fn from_wire<R: ReadZero<'wire> + ?Sized>(
                    $r_req: &mut R,
                    $a_req: &'wire dyn Arena,
                ) -> Result<Self, wire::Error> {
                    $req_from
                }
            }

            impl ToWire for Req<'_> {
                fn to_wire<W: Write>(&$self_req, mut $w_req: W) -> Result<(), wire::Error> {
                    $req_to
                }
            }

            $(
                protocol_struct!(@internal if_nonempty ($($rsp_lt)?) {
                    derive_borrowed! {
                        #[doc = "The [`" $Command "`] response."]
                        #[doc = ""]
                        #[doc = "Prefer to refer to this type as `Resp<" $Command ">`."]
                        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                        #[cfg_attr(feature = "serde", derive(serde::Serialize))]
                        $(#[$rsp_meta])*
                        #[@static(
                            derive(Clone, PartialEq, Eq, Debug),
                            cfg_attr(feature = "serde", derive(serde::Deserialize)),
                            cfg_attr(all($($($rsp_fuzz_condition)*,)? feature = "arbitrary-derive"), derive(Arbitrary)),
                        )]
                        $(#[@static($srsp_meta)])*
                        pub $rsp_kw [<$Command Response>] $(<$rsp_lt>)? {
                            $($rsp_fields)*
                        }
                    }
                    type Resp<'wire> = [<$Command Response>]<'wire>;
                } else {
                    derive_borrowed! {
                        #[doc = "The [`" $Command "`] response."]
                        #[doc = ""]
                        #[doc = "Prefer to refer to this type as `Resp<" $Command ">`."]
                        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
                        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
                        #[cfg_attr(all($($($rsp_fuzz_condition)*,)? feature = "arbitrary-derive"), derive(Arbitrary))]
                        $(#[$rsp_meta])*
                        $(#[@static($srsp_meta)])*
                        pub $rsp_kw [<$Command Response>] {
                            $($rsp_fields)*
                        }
                    }
                    type Resp<'wire> = [<$Command Response>];
                });

                impl<'wire> Message<'wire> for Resp<'wire> {
                    type CommandType = $CommandType;
                    const TYPE: $CommandType = $CommandType::$TYPE;
                }

                impl<'wire> FromWire<'wire> for Resp<'wire> {
                    fn from_wire<R: ReadZero<'wire> + ?Sized>(
                        $r_rsp: &mut R,
                        $a_rsp: &'wire dyn Arena,
                    ) -> Result<Self, wire::Error> {
                        $rsp_from
                    }
                }

                impl ToWire for Resp<'_> {
                    fn to_wire<W: Write>(&$self_rsp, mut $w_rsp: W) -> Result<(), wire::Error> {
                        $rsp_to
                    }
                }
            )*
        }
        pub use generated::*;
    }};

    (@internal if_nonempty () { $($t:tt)* } else { $($f:tt)* }) => {$($f)*};
    (@internal if_nonempty ($($nonempty:tt)+) { $($t:tt)* } else { $($f:tt)* }) => {$($t)*};
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::VecDeque;
use std::env::{set_current_dir, current_dir, home_dir};
use std::path::PathBuf;
use super::variables::Variables;
use super::status::SUCCESS;

pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    /// Create a new `DirectoryStack` containing the current working directory, if available.
    pub fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match current_dir() {
            Ok(curr_dir) => {
                dirs.push_front(curr_dir);
                DirectoryStack { dirs: dirs }
            },
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                DirectoryStack { dirs: dirs}
            }
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of the
    /// directory stack size variable. If it succeeds, it will return the value of that variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables.get_var_or_empty("DIRECTORY_STACK_SIZE").parse::<usize>().unwrap_or(1000)
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub fn popd<I: IntoIterator>(&mut self, args: I) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
	let mut keep_front = false; // whether the -n option is present
	let mut count_from_front = true; // <=> input number is positive
	let mut num: usize = 0;

	for arg in args.into_iter().skip(1) {
		let arg = arg.as_ref();
		if arg == "-n" {
			keep_front = true;
		} else {
			match parse_numeric_arg(arg) {
				Some((x, y)) => { count_from_front = x; num = y; }
				None => return Err(Cow::Owned(format!("ion: popd: {}: invalid argument\n", arg)))
			};
		}
	}

	let len: usize = self.dirs.len();
	if len <= 1 {
		return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
	}

	let mut index: usize = if count_from_front { num } else {
		(len - 1).checked_sub(num)
			.ok_or_else(|| Cow::Owned(format!("ion: popd: negative directory stack index out of range\n")))?
	};

	// apply -n
	if index == 0 && keep_front { index = 1; }

	// change to new directory, return if not possible
	if index == 0 {
		self.set_current_dir_by_index(1, "popd")?;
	}

	// pop element
	if self.dirs.remove(index).is_none() {
		return Err(Cow::Owned(format!("ion: popd: {}: directory stack index out of range\n", index)));
	}

	self.print_dirs();
	Ok(())
    }

    pub fn pushd<I: IntoIterator>(&mut self, args: I, variables: &Variables) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
        args.into_iter().nth(1)
            .map_or_else(|| { Err(Cow::Borrowed("ion: no directory provided\n")) }, |dir| {
                let result = self.change_and_push_dir(dir.as_ref(), variables);
                self.print_dirs();
                result
            })
    }

    pub fn cd<I: IntoIterator>(&mut self, args: I, variables: &Variables) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
            match args.into_iter().nth(1) {
                Some(dir) => {
                    let dir = dir.as_ref();
                    if dir == "-" {
                        self.switch_to_previous_directory(variables)
                    } else {
                        self.change_and_push_dir(dir, variables)
                    }
                }
                None => self.switch_to_home_directory(variables)
            }
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        home_dir()
            .map_or(Err(Cow::Borrowed("ion: failed to get home directory")), |home| {
                home.to_str().map_or(Err(Cow::Borrowed("ion: failed to convert home directory to str")), |home| {
                    self.change_and_push_dir(home, variables)
                })
            })
    }

    fn switch_to_previous_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir().cloned()
            .map_or_else(|| Err(Cow::Borrowed("ion: no previous directory to switch to")), |prev| {
                self.dirs.remove(1);
                let prev = prev.to_string_lossy().to_string();
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            })
    }

    fn get_previous_dir(&self) -> Option<&PathBuf> {
        if self.dirs.len() < 2 {
            None
        } else {
            self.dirs.get(1)
        }
    }

    pub fn change_and_push_dir(&mut self, dir: &str, variables: &Variables) -> Result<(), Cow<'static, str>> {
        match (set_current_dir(dir), current_dir()) {
            (Ok(()), Ok(cur_dir)) => {
                self.push_dir(cur_dir, variables);
                Ok(())
            }
            (Err(err), _) => {
                Err(Cow::Owned(format!("ion: failed to set current dir to {}: {}\n", dir, err)))
            }
            (_, _) => Err(Cow::Borrowed("ion: change_and_push_dir(): error occurred that should never happen\n")), // This should not happen
        }
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub fn dirs<I: IntoIterator>(&self, _: I) -> i32
        where I::Item: AsRef<str>
    {
        self.print_dirs();
        SUCCESS
    }

    pub fn dir_from_top(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.get(num)
    }

    pub fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_left());
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(&self, index: usize, caller: &str) -> Result<(), Cow<'static, str>> {
	let dir = self.dirs.iter().nth(index)
		.ok_or_else(|| Cow::Owned(format!("ion: {}: {}: directory stack out of range\n", caller, index)))?;

	set_current_dir(dir)
		.map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
	match arg.chars().nth(0) {
		Some('+') => Some(true),
		Some('-') => Some(false),
		_ => None
	}.and_then(|b| {
		arg[1..].parse::<usize>()
			.ok()
			.map(|num| (b, num))
	})
}
<commit_msg>implemented pushd command line options<commit_after>use std::borrow::Cow;
use std::collections::VecDeque;
use std::env::{set_current_dir, current_dir, home_dir};
use std::path::PathBuf;
use super::variables::Variables;
use super::status::SUCCESS;

pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    /// Create a new `DirectoryStack` containing the current working directory, if available.
    pub fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match current_dir() {
            Ok(curr_dir) => {
                dirs.push_front(curr_dir);
                DirectoryStack { dirs: dirs }
            },
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                DirectoryStack { dirs: dirs}
            }
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of the
    /// directory stack size variable. If it succeeds, it will return the value of that variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables.get_var_or_empty("DIRECTORY_STACK_SIZE").parse::<usize>().unwrap_or(1000)
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub fn popd<I: IntoIterator>(&mut self, args: I) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
	let mut keep_front = false; // whether the -n option is present
	let mut count_from_front = true; // <=> input number is positive
	let mut num: usize = 0;

	for arg in args.into_iter().skip(1) {
		let arg = arg.as_ref();
		if arg == "-n" {
			keep_front = true;
		} else {
			match parse_numeric_arg(arg) {
				Some((x, y)) => { count_from_front = x; num = y; }
				None => return Err(Cow::Owned(format!("ion: popd: {}: invalid argument\n", arg)))
			};
		}
	}

	let len: usize = self.dirs.len();
	if len <= 1 {
		return Err(Cow::Borrowed("ion: popd: directory stack empty\n"));
	}

	let mut index: usize = if count_from_front { num } else {
		(len - 1).checked_sub(num)
			.ok_or_else(|| Cow::Owned(format!("ion: popd: negative directory stack index out of range\n")))?
	};

	// apply -n
	if index == 0 && keep_front { index = 1; }

	// change to new directory, return if not possible
	if index == 0 {
		self.set_current_dir_by_index(1, "popd")?;
	}

	// pop element
	if self.dirs.remove(index).is_none() {
		return Err(Cow::Owned(format!("ion: popd: {}: directory stack index out of range\n", index)));
	}

	self.print_dirs();
	Ok(())
    }

    pub fn pushd<I: IntoIterator>(&mut self, args: I, variables: &Variables) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
	enum Action {
		Switch, // <no arguments>
		RotLeft(usize), // +[num]
		RotRight(usize), // -[num]
		Push(PathBuf) // [dir]
	}

	let mut keep_front = false; // whether the -n option is present
	let mut action : Action = Action::Switch;

	for arg in args.into_iter().skip(1) {
		let arg = arg.as_ref();
		if arg == "-n" {
			keep_front = true;
		} else if let Action::Switch = action { // if action is not yet defined
			action = match parse_numeric_arg(arg) {
				Some((true, num)) => Action::RotLeft(num),
				Some((false, num)) => Action::RotRight(num),
				None => Action::Push(PathBuf::from(arg)) // no numeric arg => `dir`-parameter
			};
		} else {
			return Err(Cow::Borrowed("ion: pushd: too many arguments\n"));
		}
	}

	let len = self.dirs.len();
	match action {
		Action::Switch => {
			if len < 2 {
				return Err(Cow::Borrowed("ion: pushd: no other directory\n"));
			}
			if !keep_front {
				self.set_current_dir_by_index(1, "pushd")?;
				self.dirs.swap(0, 1);
			}
		},
		Action::RotLeft(num) => {
			if !keep_front {
				self.set_current_dir_by_index(num, "pushd")?;
				self.rotate_left(num);
			}
		},
		Action::RotRight(num) => {
			if !keep_front {
				self.set_current_dir_by_index(len - (num % len), "pushd")?;
				self.rotate_right(num);
			}
		},
		Action::Push(dir) => {
			let index = if keep_front { 1 } else { 0 };
			self.insert_dir(index, dir, variables);
		}
	};

	self.print_dirs();

	Ok(())
    }

    pub fn cd<I: IntoIterator>(&mut self, args: I, variables: &Variables) -> Result<(), Cow<'static, str>>
        where I::Item: AsRef<str>
    {
            match args.into_iter().nth(1) {
                Some(dir) => {
                    let dir = dir.as_ref();
                    if dir == "-" {
                        self.switch_to_previous_directory(variables)
                    } else {
                        self.change_and_push_dir(dir, variables)
                    }
                }
                None => self.switch_to_home_directory(variables)
            }
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        home_dir()
            .map_or(Err(Cow::Borrowed("ion: failed to get home directory")), |home| {
                home.to_str().map_or(Err(Cow::Borrowed("ion: failed to convert home directory to str")), |home| {
                    self.change_and_push_dir(home, variables)
                })
            })
    }

    fn switch_to_previous_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir().cloned()
            .map_or_else(|| Err(Cow::Borrowed("ion: no previous directory to switch to")), |prev| {
                self.dirs.remove(1);
                let prev = prev.to_string_lossy().to_string();
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            })
    }

    fn get_previous_dir(&self) -> Option<&PathBuf> {
        if self.dirs.len() < 2 {
            None
        } else {
            self.dirs.get(1)
        }
    }

    pub fn change_and_push_dir(&mut self, dir: &str, variables: &Variables) -> Result<(), Cow<'static, str>> {
        match (set_current_dir(dir), current_dir()) {
            (Ok(()), Ok(cur_dir)) => {
                self.push_dir(cur_dir, variables);
                Ok(())
            }
            (Err(err), _) => {
                Err(Cow::Owned(format!("ion: failed to set current dir to {}: {}\n", dir, err)))
            }
            (_, _) => Err(Cow::Borrowed("ion: change_and_push_dir(): error occurred that should never happen\n")), // This should not happen
        }
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
        self.dirs.insert(index, path);
        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub fn dirs<I: IntoIterator>(&self, _: I) -> i32
        where I::Item: AsRef<str>
    {
        self.print_dirs();
        SUCCESS
    }

    pub fn dir_from_top(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.get(num)
    }

    pub fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_left());
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(&self, index: usize, caller: &str) -> Result<(), Cow<'static, str>> {
	let dir = self.dirs.iter().nth(index)
		.ok_or_else(|| Cow::Owned(format!("ion: {}: {}: directory stack out of range\n", caller, index)))?;

	set_current_dir(dir)
		.map_err(|_| Cow::Owned(format!("ion: {}: Failed setting current dir\n", caller)))
    }

    // pushd +<num>
    fn rotate_left(&mut self, num: usize) {
	let cloned = self.dirs.clone();
	for (dest, src) in self.dirs.iter_mut().zip(
		cloned.iter().cycle().skip(num)
	) {
		*dest = src.clone();
	}
    }

    // pushd -<num>
    fn rotate_right(&mut self, num: usize) {
	let len = self.dirs.len();
	self.rotate_left(len - (num % len));
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
	match arg.chars().nth(0) {
		Some('+') => Some(true),
		Some('-') => Some(false),
		_ => None
	}.and_then(|b| {
		arg[1..].parse::<usize>()
			.ok()
			.map(|num| (b, num))
	})
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt;
use std::env;
use std::fs;
use std::io::Read;
use std::path;

use toml;

use utils::ServerError;

#[derive(Clone)]
pub struct Config {
    pub listen: String,
    pub data_path: path::PathBuf
}

impl Config {
    pub fn read_file(path: &path::Path) -> Result<Self, ServerError> {
        let path = &env::current_dir().unwrap().join(path);
        let mut s = String::new();
        let mut f = try!(fs::File::open(path));
        try!(f.read_to_string(&mut s));

        let mut parser = toml::Parser::new(&s);
        let mut sections = match parser.parse() {
            Some(x) => x,
            None => return Err(ConfigError::new(format!("{:?}", parser.errors)).into())
        };

        let mut main_section = match sections.remove("main") {
            Some(toml::Value::Table(x)) => x,
            _ => panic!("Config file is missing `main` section.")
        };

        let listen = match main_section.remove("listen") {
            Some(toml::Value::String(x)) => x,
            _ => panic!("The `listen` parameter is missing.")
        };

        let data_path = match main_section.remove("data_path") {
            Some(toml::Value::String(x)) => path.join(&x),
            _ => panic!("The `data_path` parameter is missing.")
        };

        Ok(Config {
            listen: listen,
            data_path: data_path
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub msg: String
}

impl ConfigError {
    pub fn new(msg: String) -> Self {
        ConfigError { msg: msg }
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ConfigError {
    fn description(&self) -> &str { &self.msg[..] }
    fn cause(&self) -> Option<&Error> { None }
}

impl From<toml::ParserError> for ServerError {
    fn from(e: toml::ParserError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

impl From<ConfigError> for ServerError {
    fn from(e: ConfigError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

<commit_msg>bugfix<commit_after>use std::error::Error;
use std::fmt;
use std::env;
use std::fs;
use std::io::Read;
use std::path;

use toml;

use utils::ServerError;

#[derive(Clone)]
pub struct Config {
    pub listen: String,
    pub data_path: path::PathBuf
}

impl Config {
    pub fn read_file(path: &path::Path) -> Result<Self, ServerError> {
        let path = &env::current_dir().unwrap().join(path);
        let mut s = String::new();
        let mut f = try!(fs::File::open(path));
        try!(f.read_to_string(&mut s));

        let mut parser = toml::Parser::new(&s);
        let mut sections = match parser.parse() {
            Some(x) => x,
            None => return Err(ConfigError::new(format!("{:?}", parser.errors)).into())
        };

        let mut main_section = match sections.remove("main") {
            Some(toml::Value::Table(x)) => x,
            _ => panic!("Config file is missing `main` section.")
        };

        let listen = match main_section.remove("listen") {
            Some(toml::Value::String(x)) => x,
            _ => panic!("The `listen` parameter is missing.")
        };

        let data_path = match main_section.remove("data_path") {
            Some(toml::Value::String(x)) => path.parent().unwrap().join(&x),
            _ => panic!("The `data_path` parameter is missing.")
        };

        Ok(Config {
            listen: listen,
            data_path: data_path
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub msg: String
}

impl ConfigError {
    pub fn new(msg: String) -> Self {
        ConfigError { msg: msg }
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl Error for ConfigError {
    fn description(&self) -> &str { &self.msg[..] }
    fn cause(&self) -> Option<&Error> { None }
}

impl From<toml::ParserError> for ServerError {
    fn from(e: toml::ParserError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

impl From<ConfigError> for ServerError {
    fn from(e: ConfigError) -> ServerError {
        ServerError { error: Box::new(e) }
    }
}

<|endoftext|>


<commit_before>#![feature(const_fn)]
#![feature(slice_patterns)]
#![cfg_attr(test, feature(test))]

pub mod color;
pub mod matrix;
pub mod orientation;
pub mod point;
pub mod quaternion;
pub mod vector;

#[cfg(test)]
mod test;

pub use point::Point;
pub use vector::{Vector2, Vector3};
pub use matrix::{Matrix3, Matrix4};
pub use color::Color;
pub use orientation::Orientation;

pub use std::f32::consts::PI;

pub const EPSILON: f32 = 1e-6;

pub trait IsZero {
    fn is_zero(self) -> bool;
}

impl IsZero for f32 {
    fn is_zero(self) -> bool {
        self.abs() < EPSILON
    }
}

pub trait Clamp {
    fn clamp(self, min: Self, max: Self) -> Self;
}

impl Clamp for f32 {
    fn clamp(self, min: f32, max: f32) -> f32 {
        f32::min(f32::max(self, min), max)
    }
}

pub trait Dot<Other=Self> {
    type Output;

    fn dot(self, rhs: Other) -> Self::Output;
}

impl Dot for [f32; 3] {
    type Output = f32;

    fn dot(self, rhs: [f32; 3]) -> f32 {
        self[0] * rhs[0]
      + self[1] * rhs[1]
      + self[2] * rhs[2]
    }
}

// Doesn't cause ICE.
impl<'a, T> Dot<&'a T> for T where T: Dot<T> + Copy {
    type Output = T::Output;

    fn dot(self, rhs: &T) -> Self::Output {
        self.dot(*rhs)
    }
}

// // Causes ICE.
// impl<'a, T, U> Dot<&'a U> for T where T: Dot<U>, U: Copy {
//     type Output = T::Output;
//
//     fn dot(self, rhs: &U) -> Self::Output {
//         self.dot(*rhs)
//     }
// }

pub trait Lerp {
    fn lerp(t: f32, from: Self, to: Self) -> Self;
}

impl Lerp for f32 {
    fn lerp(t: f32, from: f32, to: f32) -> f32 {
        from + (to - from) * t
    }
}
<commit_msg>Add TAU constant to polygon-math<commit_after>#![feature(const_fn)]
#![feature(slice_patterns)]
#![cfg_attr(test, feature(test))]

pub mod color;
pub mod matrix;
pub mod orientation;
pub mod point;
pub mod quaternion;
pub mod vector;

#[cfg(test)]
mod test;

pub use point::Point;
pub use vector::{Vector2, Vector3};
pub use matrix::{Matrix3, Matrix4};
pub use color::Color;
pub use orientation::Orientation;

pub use std::f32::consts::PI;

pub const TAU: f32 = 2.0 * PI;

pub const EPSILON: f32 = 1e-6;

pub trait IsZero {
    fn is_zero(self) -> bool;
}

impl IsZero for f32 {
    fn is_zero(self) -> bool {
        self.abs() < EPSILON
    }
}

pub trait Clamp {
    fn clamp(self, min: Self, max: Self) -> Self;
}

impl Clamp for f32 {
    fn clamp(self, min: f32, max: f32) -> f32 {
        f32::min(f32::max(self, min), max)
    }
}

pub trait Dot<Other=Self> {
    type Output;

    fn dot(self, rhs: Other) -> Self::Output;
}

impl Dot for [f32; 3] {
    type Output = f32;

    fn dot(self, rhs: [f32; 3]) -> f32 {
        self[0] * rhs[0]
      + self[1] * rhs[1]
      + self[2] * rhs[2]
    }
}

// Doesn't cause ICE.
impl<'a, T> Dot<&'a T> for T where T: Dot<T> + Copy {
    type Output = T::Output;

    fn dot(self, rhs: &T) -> Self::Output {
        self.dot(*rhs)
    }
}

// // Causes ICE.
// impl<'a, T, U> Dot<&'a U> for T where T: Dot<U>, U: Copy {
//     type Output = T::Output;
//
//     fn dot(self, rhs: &U) -> Self::Output {
//         self.dot(*rhs)
//     }
// }

pub trait Lerp {
    fn lerp(t: f32, from: Self, to: Self) -> Self;
}

impl Lerp for f32 {
    fn lerp(t: f32, from: f32, to: f32) -> f32 {
        from + (to - from) * t
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! Aptos Rosetta API
//!
//! [Rosetta API Spec](https://www.rosetta-api.org/docs/Reference.html)

use crate::{
    account::CoinCache,
    block::BlockRetriever,
    common::{handle_request, with_context},
    error::{ApiError, ApiResult},
};
use aptos_config::config::ApiConfig;
use aptos_logger::debug;
use aptos_types::account_address::AccountAddress;
use aptos_types::chain_id::ChainId;
use aptos_warp_webserver::WebServer;
use aptos_warp_webserver::{logger, Error};
use std::collections::BTreeMap;
use std::{convert::Infallible, sync::Arc};
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use warp::{
    http::{HeaderValue, Method, StatusCode},
    reject::{MethodNotAllowed, PayloadTooLarge, UnsupportedMediaType},
    reply, Filter, Rejection, Reply,
};

mod account;
mod block;
mod construction;
mod network;

pub mod client;
pub mod common;
pub mod error;
pub mod types;

pub const NODE_VERSION: &str = "0.1";
pub const ROSETTA_VERSION: &str = "1.4.12";

type SequenceNumber = u64;

/// Rosetta API context for use on all APIs
#[derive(Clone, Debug)]
pub struct RosettaContext {
    /// A rest client to connect to a fullnode
    rest_client: Option<Arc<aptos_rest_client::Client>>,
    /// ChainId of the chain to connect to
    pub chain_id: ChainId,
    /// Coin cache for looking up Currency details
    pub coin_cache: Arc<CoinCache>,
    /// Block index cache
    pub block_cache: Option<Arc<BlockRetriever>>,
    pub accounts: Arc<Mutex<BTreeMap<AccountAddress, SequenceNumber>>>,
}

impl RosettaContext {
    fn rest_client(&self) -> ApiResult<Arc<aptos_rest_client::Client>> {
        if let Some(ref client) = self.rest_client {
            Ok(client.clone())
        } else {
            Err(ApiError::NodeIsOffline)
        }
    }

    fn block_cache(&self) -> ApiResult<Arc<BlockRetriever>> {
        if let Some(ref block_cache) = self.block_cache {
            Ok(block_cache.clone())
        } else {
            Err(ApiError::NodeIsOffline)
        }
    }
}

/// Creates HTTP server (warp-based) for Rosetta
pub fn bootstrap(
    chain_id: ChainId,
    api_config: ApiConfig,
    rest_client: Option<aptos_rest_client::Client>,
) -> anyhow::Result<tokio::runtime::Runtime> {
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .thread_name("rosetta")
        .enable_all()
        .build()
        .expect("[rosetta] failed to create runtime");

    debug!("Starting up Rosetta server with {:?}", api_config);

    runtime.spawn(bootstrap_async(chain_id, api_config, rest_client));
    Ok(runtime)
}

/// Creates HTTP server for Rosetta in an async context
pub async fn bootstrap_async(
    chain_id: ChainId,
    api_config: ApiConfig,
    rest_client: Option<aptos_rest_client::Client>,
) -> anyhow::Result<JoinHandle<()>> {
    debug!("Starting up Rosetta server with {:?}", api_config);
    let api = WebServer::from(api_config);
    let handle = tokio::spawn(async move {
        // If it's Online mode, add the block cache
        let rest_client = rest_client.map(Arc::new);
        let block_cache = rest_client
            .as_ref()
            .map(|rest_client| Arc::new(BlockRetriever::new(rest_client.clone())));

        let context = RosettaContext {
            rest_client: rest_client.clone(),
            chain_id,
            coin_cache: Arc::new(CoinCache::new()),
            block_cache,
            accounts: Arc::new(Mutex::new(BTreeMap::new())),
        };
        api.serve(routes(context)).await;
    });
    Ok(handle)
}

/// Collection of all routes for the server
pub fn routes(
    context: RosettaContext,
) -> impl Filter<Extract = impl Reply, Error = Infallible> + Clone {
    account::routes(context.clone())
        .or(block::block_route(context.clone()))
        .or(construction::combine_route(context.clone()))
        .or(construction::derive_route(context.clone()))
        .or(construction::hash_route(context.clone()))
        .or(construction::metadata_route(context.clone()))
        .or(construction::parse_route(context.clone()))
        .or(construction::payloads_route(context.clone()))
        .or(construction::preprocess_route(context.clone()))
        .or(construction::submit_route(context.clone()))
        .or(network::list_route(context.clone()))
        .or(network::options_route(context.clone()))
        .or(network::status_route(context.clone()))
        .or(health_check_route(context))
        .with(
            warp::cors()
                .allow_any_origin()
                .allow_methods(vec![Method::GET, Method::POST])
                .allow_headers(vec![warp::http::header::CONTENT_TYPE]),
        )
        .with(logger())
        .recover(handle_rejection)
}

/// Handle error codes from warp
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let code;
    let body;

    debug!("Failed with: {:?}", err);

    if err.is_not_found() {
        code = StatusCode::NOT_FOUND;
        body = reply::json(&Error::new(code, "Not Found".to_owned()));
    } else if let Some(cause) = err.find::<warp::cors::CorsForbidden>() {
        code = StatusCode::FORBIDDEN;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<warp::body::BodyDeserializeError>() {
        code = StatusCode::BAD_REQUEST;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<warp::reject::LengthRequired>() {
        code = StatusCode::LENGTH_REQUIRED;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<PayloadTooLarge>() {
        code = StatusCode::PAYLOAD_TOO_LARGE;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<UnsupportedMediaType>() {
        code = StatusCode::UNSUPPORTED_MEDIA_TYPE;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<MethodNotAllowed>() {
        code = StatusCode::METHOD_NOT_ALLOWED;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else {
        code = StatusCode::INTERNAL_SERVER_ERROR;
        body = reply::json(&Error::new(code, format!("unexpected error: {:?}", err)));
    }
    let mut rep = reply::with_status(body, code).into_response();
    rep.headers_mut()
        .insert("access-control-allow-origin", HeaderValue::from_static("*"));
    Ok(rep)
}

/// These parameters are directly passed onto the underlying rest server for a healthcheck
#[derive(serde::Deserialize)]
struct HealthCheckParams {
    pub duration_secs: Option<u64>,
}

/// Default amount of time the fullnode is accepted to be behind (arbitrarily it's 5 minutes)
const HEALTH_CHECK_DEFAULT_SECS: u64 = 300;

pub fn health_check_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("-" / "healthy")
        .and(warp::path::end())
        .and(warp::query().map(move |params: HealthCheckParams| params))
        .and(with_context(server_context))
        .and_then(handle_request(health_check))
}

/// Calls the underlying REST health check
async fn health_check(
    params: HealthCheckParams,
    server_context: RosettaContext,
) -> ApiResult<&'static str> {
    let rest_client = server_context.rest_client()?;
    let duration_secs = params.duration_secs.unwrap_or(HEALTH_CHECK_DEFAULT_SECS);
    rest_client.health_check(duration_secs).await?;

    Ok("aptos-node:ok")
}
<commit_msg>[aptos-rosetta] Ensure chain id matches node in online mode<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! Aptos Rosetta API
//!
//! [Rosetta API Spec](https://www.rosetta-api.org/docs/Reference.html)

use crate::{
    account::CoinCache,
    block::BlockRetriever,
    common::{handle_request, with_context},
    error::{ApiError, ApiResult},
};
use aptos_config::config::ApiConfig;
use aptos_logger::debug;
use aptos_types::account_address::AccountAddress;
use aptos_types::chain_id::ChainId;
use aptos_warp_webserver::WebServer;
use aptos_warp_webserver::{logger, Error};
use std::collections::BTreeMap;
use std::{convert::Infallible, sync::Arc};
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use warp::{
    http::{HeaderValue, Method, StatusCode},
    reject::{MethodNotAllowed, PayloadTooLarge, UnsupportedMediaType},
    reply, Filter, Rejection, Reply,
};

mod account;
mod block;
mod construction;
mod network;

pub mod client;
pub mod common;
pub mod error;
pub mod types;

pub const NODE_VERSION: &str = "0.1";
pub const ROSETTA_VERSION: &str = "1.4.12";

type SequenceNumber = u64;

/// Rosetta API context for use on all APIs
#[derive(Clone, Debug)]
pub struct RosettaContext {
    /// A rest client to connect to a fullnode
    rest_client: Option<Arc<aptos_rest_client::Client>>,
    /// ChainId of the chain to connect to
    pub chain_id: ChainId,
    /// Coin cache for looking up Currency details
    pub coin_cache: Arc<CoinCache>,
    /// Block index cache
    pub block_cache: Option<Arc<BlockRetriever>>,
    pub accounts: Arc<Mutex<BTreeMap<AccountAddress, SequenceNumber>>>,
}

impl RosettaContext {
    fn rest_client(&self) -> ApiResult<Arc<aptos_rest_client::Client>> {
        if let Some(ref client) = self.rest_client {
            Ok(client.clone())
        } else {
            Err(ApiError::NodeIsOffline)
        }
    }

    fn block_cache(&self) -> ApiResult<Arc<BlockRetriever>> {
        if let Some(ref block_cache) = self.block_cache {
            Ok(block_cache.clone())
        } else {
            Err(ApiError::NodeIsOffline)
        }
    }
}

/// Creates HTTP server (warp-based) for Rosetta
pub fn bootstrap(
    chain_id: ChainId,
    api_config: ApiConfig,
    rest_client: Option<aptos_rest_client::Client>,
) -> anyhow::Result<tokio::runtime::Runtime> {
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .thread_name("rosetta")
        .enable_all()
        .build()
        .expect("[rosetta] failed to create runtime");

    debug!("Starting up Rosetta server with {:?}", api_config);

    runtime.spawn(bootstrap_async(chain_id, api_config, rest_client));
    Ok(runtime)
}

/// Creates HTTP server for Rosetta in an async context
pub async fn bootstrap_async(
    chain_id: ChainId,
    api_config: ApiConfig,
    rest_client: Option<aptos_rest_client::Client>,
) -> anyhow::Result<JoinHandle<()>> {
    debug!("Starting up Rosetta server with {:?}", api_config);

    if let Some(ref client) = rest_client {
        assert_eq!(
            chain_id.id(),
            client
                .get_ledger_information()
                .await
                .expect("Should successfully get ledger information from Rest API")
                .into_inner()
                .chain_id,
            "Failed to match Rosetta chain Id to upstream server"
        );
    }

    let api = WebServer::from(api_config);
    let handle = tokio::spawn(async move {
        // If it's Online mode, add the block cache
        let rest_client = rest_client.map(Arc::new);
        let block_cache = rest_client
            .as_ref()
            .map(|rest_client| Arc::new(BlockRetriever::new(rest_client.clone())));

        let context = RosettaContext {
            rest_client: rest_client.clone(),
            chain_id,
            coin_cache: Arc::new(CoinCache::new()),
            block_cache,
            accounts: Arc::new(Mutex::new(BTreeMap::new())),
        };
        api.serve(routes(context)).await;
    });
    Ok(handle)
}

/// Collection of all routes for the server
pub fn routes(
    context: RosettaContext,
) -> impl Filter<Extract = impl Reply, Error = Infallible> + Clone {
    account::routes(context.clone())
        .or(block::block_route(context.clone()))
        .or(construction::combine_route(context.clone()))
        .or(construction::derive_route(context.clone()))
        .or(construction::hash_route(context.clone()))
        .or(construction::metadata_route(context.clone()))
        .or(construction::parse_route(context.clone()))
        .or(construction::payloads_route(context.clone()))
        .or(construction::preprocess_route(context.clone()))
        .or(construction::submit_route(context.clone()))
        .or(network::list_route(context.clone()))
        .or(network::options_route(context.clone()))
        .or(network::status_route(context.clone()))
        .or(health_check_route(context))
        .with(
            warp::cors()
                .allow_any_origin()
                .allow_methods(vec![Method::GET, Method::POST])
                .allow_headers(vec![warp::http::header::CONTENT_TYPE]),
        )
        .with(logger())
        .recover(handle_rejection)
}

/// Handle error codes from warp
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let code;
    let body;

    debug!("Failed with: {:?}", err);

    if err.is_not_found() {
        code = StatusCode::NOT_FOUND;
        body = reply::json(&Error::new(code, "Not Found".to_owned()));
    } else if let Some(cause) = err.find::<warp::cors::CorsForbidden>() {
        code = StatusCode::FORBIDDEN;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<warp::body::BodyDeserializeError>() {
        code = StatusCode::BAD_REQUEST;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<warp::reject::LengthRequired>() {
        code = StatusCode::LENGTH_REQUIRED;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<PayloadTooLarge>() {
        code = StatusCode::PAYLOAD_TOO_LARGE;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<UnsupportedMediaType>() {
        code = StatusCode::UNSUPPORTED_MEDIA_TYPE;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else if let Some(cause) = err.find::<MethodNotAllowed>() {
        code = StatusCode::METHOD_NOT_ALLOWED;
        body = reply::json(&Error::new(code, cause.to_string()));
    } else {
        code = StatusCode::INTERNAL_SERVER_ERROR;
        body = reply::json(&Error::new(code, format!("unexpected error: {:?}", err)));
    }
    let mut rep = reply::with_status(body, code).into_response();
    rep.headers_mut()
        .insert("access-control-allow-origin", HeaderValue::from_static("*"));
    Ok(rep)
}

/// These parameters are directly passed onto the underlying rest server for a healthcheck
#[derive(serde::Deserialize)]
struct HealthCheckParams {
    pub duration_secs: Option<u64>,
}

/// Default amount of time the fullnode is accepted to be behind (arbitrarily it's 5 minutes)
const HEALTH_CHECK_DEFAULT_SECS: u64 = 300;

pub fn health_check_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("-" / "healthy")
        .and(warp::path::end())
        .and(warp::query().map(move |params: HealthCheckParams| params))
        .and(with_context(server_context))
        .and_then(handle_request(health_check))
}

/// Calls the underlying REST health check
async fn health_check(
    params: HealthCheckParams,
    server_context: RosettaContext,
) -> ApiResult<&'static str> {
    let rest_client = server_context.rest_client()?;
    let duration_secs = params.duration_secs.unwrap_or(HEALTH_CHECK_DEFAULT_SECS);
    rest_client.health_check(duration_secs).await?;

    Ok("aptos-node:ok")
}
<|endoftext|>


<commit_before>// Reference: Section 5.4.4 "LDREX / STREX" of ACLE

/// Removes the exclusive lock created by LDREX
// Supported: v6, v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __clrex() {
    extern "C" {
        #[link_name = "llvm.arm.clrex"]
        fn clrex();
    }

    clrex()
}

/// Executes a exclusive LDR instruction for 8 bit value.
// Supported: v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __ldrexb(p: *const u8) -> u8 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i8"]
        fn ldrex8(p: *const u8) -> u32;
    }

    ldrex8(p) as u8
}

/// Executes a exclusive LDR instruction for 16 bit value.
// Supported: v6K, v7-M, v7-A, v7-R, v8
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __ldrexh(p: *const u16) -> u16 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i16"]
        fn ldrex16(p: *const u16) -> u32;
    }

    ldrex16(p) as u16
}

/// Executes a exclusive LDR instruction for 32 bit value.
// Supported: v6, v7-M, v6K, v7-A, v7-R, v8
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __ldrex(p: *const u32) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i32"]
        fn ldrex32(p: *const u32) -> u32;
    }

    ldrex32(p)
}

/// Executes a exclusive STR instruction for 8 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// supported: v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __strexb(value: u32, addr: *const u8) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i8"]
        fn strex8(value: u32, addr: *const u8) -> u32;
    }

    strex8(value, addr)
}

/// Executes a exclusive STR instruction for 16 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// Supported: v6K, v7-M, v7-A, v7-R, v8
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __strexh(value: u16, addr: *const u16) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i16"]
        fn strex16(value: u32, addr: *const u16) -> u32;
    }

    strex16(value as u32, addr)
}

/// Executes a exclusive STR instruction for 32 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// Supported: v6, v7-M, v6K, v7-A, v7-R, v8
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __strex(value: u32, addr: *const u32) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i32"]
        fn strex32(value: u32, addr: *const u32) -> u32;
    }

    strex32(value, addr)
}
<commit_msg>acle/ex: fix raw pointer mutability<commit_after>// Reference: Section 5.4.4 "LDREX / STREX" of ACLE

/// Removes the exclusive lock created by LDREX
// Supported: v6, v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __clrex() {
    extern "C" {
        #[link_name = "llvm.arm.clrex"]
        fn clrex();
    }

    clrex()
}

/// Executes a exclusive LDR instruction for 8 bit value.
// Supported: v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __ldrexb(p: *const u8) -> u8 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i8"]
        fn ldrex8(p: *const u8) -> u32;
    }

    ldrex8(p) as u8
}

/// Executes a exclusive LDR instruction for 16 bit value.
// Supported: v6K, v7-M, v7-A, v7-R, v8
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __ldrexh(p: *const u16) -> u16 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i16"]
        fn ldrex16(p: *const u16) -> u32;
    }

    ldrex16(p) as u16
}

/// Executes a exclusive LDR instruction for 32 bit value.
// Supported: v6, v7-M, v6K, v7-A, v7-R, v8
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __ldrex(p: *const u32) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.ldrex.p0i32"]
        fn ldrex32(p: *const u32) -> u32;
    }

    ldrex32(p)
}

/// Executes a exclusive STR instruction for 8 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// supported: v6K, v7-M, v7-A, v7-R
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __strexb(value: u32, addr: *mut u8) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i8"]
        fn strex8(value: u32, addr: *mut u8) -> u32;
    }

    strex8(value, addr)
}

/// Executes a exclusive STR instruction for 16 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// Supported: v6K, v7-M, v7-A, v7-R, v8
// Not supported: v5, v6, v6-M
#[cfg(
    target_feature = "v6k", // includes v7-M but excludes v6-M
)]
pub unsafe fn __strexh(value: u16, addr: *mut u16) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i16"]
        fn strex16(value: u32, addr: *mut u16) -> u32;
    }

    strex16(value as u32, addr)
}

/// Executes a exclusive STR instruction for 32 bit values
///
/// Returns `0` if the operation succeeded, or `1` if it failed
// Supported: v6, v7-M, v6K, v7-A, v7-R, v8
// Not supported: v5, v6-M
#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")), // excludes v6-M
    all(target_feature = "v7", target_feature = "mclass"), // v7-M
))]
pub unsafe fn __strex(value: u32, addr: *mut u32) -> u32 {
    extern "C" {
        #[link_name = "llvm.arm.strex.p0i32"]
        fn strex32(value: u32, addr: *mut u32) -> u32;
    }

    strex32(value, addr)
}
<|endoftext|>


<commit_before>//! This module implements syntax validation that the parser doesn't handle.
//!
//! A failed validation emits a diagnostic.

mod block;

use rowan::Direction;
use rustc_lexer::unescape::{
    self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
};

use crate::{
    algo,
    ast::{self, HasVisibility},
    match_ast, AstNode, SyntaxError,
    SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
    SyntaxNode, SyntaxToken, TextSize, T,
};

pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
    // FIXME:
    // * Add unescape validation of raw string literals and raw byte string literals
    // * Add validation of doc comments are being attached to nodes

    let mut errors = Vec::new();
    for node in root.descendants() {
        match_ast! {
            match node {
                ast::Literal(it) => validate_literal(it, &mut errors),
                ast::Const(it) => validate_const(it, &mut errors),
                ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors),
                ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::Visibility(it) => validate_visibility(it, &mut errors),
                ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
                ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
                ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
                ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
                ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
                ast::LetExpr(it) => validate_let_expr(it, &mut errors),
                _ => (),
            }
        }
    }
    errors
}

fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
    use unescape::EscapeError as EE;

    #[rustfmt::skip]
    let err_message = match err {
        EE::ZeroChars => {
            "Literal must not be empty"
        }
        EE::MoreThanOneChar => {
            "Literal must be one character long"
        }
        EE::LoneSlash => {
            "Character must be escaped: `\\`"
        }
        EE::InvalidEscape => {
            "Invalid escape"
        }
        EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => {
            "Character must be escaped: `\r`"
        }
        EE::EscapeOnlyChar => {
            "Escape character `\\` must be escaped itself"
        }
        EE::TooShortHexEscape => {
            "ASCII hex escape code must have exactly two digits"
        }
        EE::InvalidCharInHexEscape => {
            "ASCII hex escape code must contain only hex characters"
        }
        EE::OutOfRangeHexEscape => {
            "ASCII hex escape code must be at most 0x7F"
        }
        EE::NoBraceInUnicodeEscape => {
            "Missing `{` to begin the unicode escape"
        }
        EE::InvalidCharInUnicodeEscape => {
            "Unicode escape must contain only hex characters and underscores"
        }
        EE::EmptyUnicodeEscape => {
            "Unicode escape must not be empty"
        }
        EE::UnclosedUnicodeEscape => {
            "Missing `}` to terminate the unicode escape"
        }
        EE::LeadingUnderscoreUnicodeEscape => {
            "Unicode escape code must not begin with an underscore"
        }
        EE::OverlongUnicodeEscape => {
            "Unicode escape code must have at most 6 digits"
        }
        EE::LoneSurrogateUnicodeEscape => {
            "Unicode escape code must not be a surrogate"
        }
        EE::OutOfRangeUnicodeEscape => {
            "Unicode escape code must be at most 0x10FFFF"
        }
        EE::UnicodeEscapeInByte => {
            "Byte literals must not contain unicode escapes"
        }
        EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
            "Byte literals must not contain non-ASCII characters"
        }
    };

    err_message
}

fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
    // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658)
    fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
        text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
    }

    let token = literal.token();
    let text = token.text();

    // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
    let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
        let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
    };

    match literal.kind() {
        ast::LiteralKind::String(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 1, '"') {
                    unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(1, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::ByteString(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 2, '"') {
                    unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(2, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::Char => {
            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                push_err(1, e);
            }
        }
        ast::LiteralKind::Byte => {
            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                push_err(2, e);
            }
        }
        ast::LiteralKind::IntNumber(_)
        | ast::LiteralKind::FloatNumber(_)
        | ast::LiteralKind::Bool(_) => {}
    }
}

pub(crate) fn validate_block_structure(root: &SyntaxNode) {
    let mut stack = Vec::new();
    for node in root.descendants_with_tokens() {
        match node.kind() {
            T!['{'] => stack.push(node),
            T!['}'] => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curlys:\n{}\n{:#?}\n",
                        root.text(),
                        root,
                    );
                    assert!(
                        node.next_sibling_or_token().is_none()
                            && pair.prev_sibling_or_token().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node,
                    );
                }
            }
            _ => (),
        }
    }
}

fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
    if let Some(int_token) = int_token(name_ref) {
        if int_token.text().chars().any(|c| !c.is_digit(10)) {
            errors.push(SyntaxError::new(
                "Tuple (struct) field access is only allowed through \
                decimal integers with no underscores or suffix",
                int_token.text_range(),
            ));
        }
    }

    fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
        name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
    }
}

fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
    let path_without_in_token = vis.in_token().is_none()
        && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
    if path_without_in_token {
        errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
    }
    let parent = match vis.syntax().parent() {
        Some(it) => it,
        None => return,
    };
    match parent.kind() {
        FN | CONST | TYPE_ALIAS => (),
        _ => return,
    }

    let impl_def = match parent.parent().and_then(|it| it.parent()).and_then(ast::Impl::cast) {
        Some(it) => it,
        None => return,
    };
    if impl_def.trait_().is_some() {
        errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
    }
}

fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
    if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
        errors.push(SyntaxError::new(
            "An inclusive range must have an end expression",
            expr.syntax().text_range(),
        ));
    }
}

fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
    let path = segment.parent_path();
    let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();

    if let Some(token) = segment.self_token() {
        if !is_path_start {
            errors.push(SyntaxError::new(
                "The `self` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    } else if let Some(token) = segment.crate_token() {
        if !is_path_start || use_prefix(path).is_some() {
            errors.push(SyntaxError::new(
                "The `crate` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    }

    fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
        for node in path.syntax().ancestors().skip(1) {
            match_ast! {
                match node {
                    ast::UseTree(it) => if let Some(tree_path) = it.path() {
                        // Even a top-level path exists within a `UseTree` so we must explicitly
                        // allow our path but disallow anything else
                        if tree_path != path {
                            return Some(tree_path);
                        }
                    },
                    ast::UseTreeList(_) => continue,
                    ast::Path(parent) => path = parent,
                    _ => return None,
                }
            };
        }
        None
    }
}

fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
    let tbl = ty.type_bound_list()?;

    if tbl.bounds().count() > 1 {
        let dyn_token = ty.dyn_token()?;
        let potential_parenthesis =
            algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
        let kind = potential_parenthesis.kind();
        if !matches!(kind, T!['('] | T![<] | T![=]) {
            return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
        }
    }
    None
}

fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
    if let Some(vis) = mac.visibility() {
        errors.push(SyntaxError::new(
            "visibilities are not allowed on `macro_rules!` items",
            vis.syntax().text_range(),
        ));
    }
}

fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
    if let Some(mut_token) = const_
        .const_token()
        .and_then(|t| t.next_token())
        .and_then(|t| algo::skip_trivia_token(t, Direction::Next))
        .filter(|t| t.kind() == T![mut])
    {
        errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
    }
}

fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
    let mut token = let_.syntax().clone();
    loop {
        token = match token.parent() {
            Some(it) => it,
            None => break,
        };

        if ast::ParenExpr::can_cast(token.kind()) {
            continue;
        } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
            if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
                continue;
            }
        } else if ast::IfExpr::can_cast(token.kind())
            || ast::WhileExpr::can_cast(token.kind())
            || ast::MatchGuard::can_cast(token.kind())
        {
            // It must be part of the condition since the expressions are inside a block.
            return;
        }

        break;
    }
    errors.push(SyntaxError::new(
        "`let` expressions are not supported here",
        let_.syntax().text_range(),
    ));
}
<commit_msg>Auto merge of #12057 - harpsword:fix_validata_visibility, r=jonas-schievink<commit_after>//! This module implements syntax validation that the parser doesn't handle.
//!
//! A failed validation emits a diagnostic.

mod block;

use rowan::Direction;
use rustc_lexer::unescape::{
    self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
};

use crate::{
    algo,
    ast::{self, HasAttrs, HasVisibility},
    match_ast, AstNode, SyntaxError,
    SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
    SyntaxNode, SyntaxToken, TextSize, T,
};

pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
    // FIXME:
    // * Add unescape validation of raw string literals and raw byte string literals
    // * Add validation of doc comments are being attached to nodes

    let mut errors = Vec::new();
    for node in root.descendants() {
        match_ast! {
            match node {
                ast::Literal(it) => validate_literal(it, &mut errors),
                ast::Const(it) => validate_const(it, &mut errors),
                ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors),
                ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::Visibility(it) => validate_visibility(it, &mut errors),
                ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
                ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
                ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
                ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
                ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
                ast::LetExpr(it) => validate_let_expr(it, &mut errors),
                _ => (),
            }
        }
    }
    errors
}

fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
    use unescape::EscapeError as EE;

    #[rustfmt::skip]
    let err_message = match err {
        EE::ZeroChars => {
            "Literal must not be empty"
        }
        EE::MoreThanOneChar => {
            "Literal must be one character long"
        }
        EE::LoneSlash => {
            "Character must be escaped: `\\`"
        }
        EE::InvalidEscape => {
            "Invalid escape"
        }
        EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => {
            "Character must be escaped: `\r`"
        }
        EE::EscapeOnlyChar => {
            "Escape character `\\` must be escaped itself"
        }
        EE::TooShortHexEscape => {
            "ASCII hex escape code must have exactly two digits"
        }
        EE::InvalidCharInHexEscape => {
            "ASCII hex escape code must contain only hex characters"
        }
        EE::OutOfRangeHexEscape => {
            "ASCII hex escape code must be at most 0x7F"
        }
        EE::NoBraceInUnicodeEscape => {
            "Missing `{` to begin the unicode escape"
        }
        EE::InvalidCharInUnicodeEscape => {
            "Unicode escape must contain only hex characters and underscores"
        }
        EE::EmptyUnicodeEscape => {
            "Unicode escape must not be empty"
        }
        EE::UnclosedUnicodeEscape => {
            "Missing `}` to terminate the unicode escape"
        }
        EE::LeadingUnderscoreUnicodeEscape => {
            "Unicode escape code must not begin with an underscore"
        }
        EE::OverlongUnicodeEscape => {
            "Unicode escape code must have at most 6 digits"
        }
        EE::LoneSurrogateUnicodeEscape => {
            "Unicode escape code must not be a surrogate"
        }
        EE::OutOfRangeUnicodeEscape => {
            "Unicode escape code must be at most 0x10FFFF"
        }
        EE::UnicodeEscapeInByte => {
            "Byte literals must not contain unicode escapes"
        }
        EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
            "Byte literals must not contain non-ASCII characters"
        }
    };

    err_message
}

fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
    // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658)
    fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
        text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
    }

    let token = literal.token();
    let text = token.text();

    // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
    let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
        let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
    };

    match literal.kind() {
        ast::LiteralKind::String(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 1, '"') {
                    unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(1, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::ByteString(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 2, '"') {
                    unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(2, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::Char => {
            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                push_err(1, e);
            }
        }
        ast::LiteralKind::Byte => {
            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                push_err(2, e);
            }
        }
        ast::LiteralKind::IntNumber(_)
        | ast::LiteralKind::FloatNumber(_)
        | ast::LiteralKind::Bool(_) => {}
    }
}

pub(crate) fn validate_block_structure(root: &SyntaxNode) {
    let mut stack = Vec::new();
    for node in root.descendants_with_tokens() {
        match node.kind() {
            T!['{'] => stack.push(node),
            T!['}'] => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curlys:\n{}\n{:#?}\n",
                        root.text(),
                        root,
                    );
                    assert!(
                        node.next_sibling_or_token().is_none()
                            && pair.prev_sibling_or_token().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node,
                    );
                }
            }
            _ => (),
        }
    }
}

fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
    if let Some(int_token) = int_token(name_ref) {
        if int_token.text().chars().any(|c| !c.is_digit(10)) {
            errors.push(SyntaxError::new(
                "Tuple (struct) field access is only allowed through \
                decimal integers with no underscores or suffix",
                int_token.text_range(),
            ));
        }
    }

    fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
        name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
    }
}

fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
    let path_without_in_token = vis.in_token().is_none()
        && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
    if path_without_in_token {
        errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
    }
    let parent = match vis.syntax().parent() {
        Some(it) => it,
        None => return,
    };
    match parent.kind() {
        FN | CONST | TYPE_ALIAS => (),
        _ => return,
    }

    let impl_def = match parent.parent().and_then(|it| it.parent()).and_then(ast::Impl::cast) {
        Some(it) => it,
        None => return,
    };
    // FIXME: disable validation if there's an attribute, since some proc macros use this syntax.
    // ideally the validation would run only on the fully expanded code, then this wouldn't be necessary.
    if impl_def.trait_().is_some() && impl_def.attrs().next().is_none() {
        errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
    }
}

fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
    if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
        errors.push(SyntaxError::new(
            "An inclusive range must have an end expression",
            expr.syntax().text_range(),
        ));
    }
}

fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
    let path = segment.parent_path();
    let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();

    if let Some(token) = segment.self_token() {
        if !is_path_start {
            errors.push(SyntaxError::new(
                "The `self` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    } else if let Some(token) = segment.crate_token() {
        if !is_path_start || use_prefix(path).is_some() {
            errors.push(SyntaxError::new(
                "The `crate` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    }

    fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
        for node in path.syntax().ancestors().skip(1) {
            match_ast! {
                match node {
                    ast::UseTree(it) => if let Some(tree_path) = it.path() {
                        // Even a top-level path exists within a `UseTree` so we must explicitly
                        // allow our path but disallow anything else
                        if tree_path != path {
                            return Some(tree_path);
                        }
                    },
                    ast::UseTreeList(_) => continue,
                    ast::Path(parent) => path = parent,
                    _ => return None,
                }
            };
        }
        None
    }
}

fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
    let tbl = ty.type_bound_list()?;

    if tbl.bounds().count() > 1 {
        let dyn_token = ty.dyn_token()?;
        let potential_parenthesis =
            algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
        let kind = potential_parenthesis.kind();
        if !matches!(kind, T!['('] | T![<] | T![=]) {
            return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
        }
    }
    None
}

fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
    if let Some(vis) = mac.visibility() {
        errors.push(SyntaxError::new(
            "visibilities are not allowed on `macro_rules!` items",
            vis.syntax().text_range(),
        ));
    }
}

fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
    if let Some(mut_token) = const_
        .const_token()
        .and_then(|t| t.next_token())
        .and_then(|t| algo::skip_trivia_token(t, Direction::Next))
        .filter(|t| t.kind() == T![mut])
    {
        errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
    }
}

fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
    let mut token = let_.syntax().clone();
    loop {
        token = match token.parent() {
            Some(it) => it,
            None => break,
        };

        if ast::ParenExpr::can_cast(token.kind()) {
            continue;
        } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
            if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
                continue;
            }
        } else if ast::IfExpr::can_cast(token.kind())
            || ast::WhileExpr::can_cast(token.kind())
            || ast::MatchGuard::can_cast(token.kind())
        {
            // It must be part of the condition since the expressions are inside a block.
            return;
        }

        break;
    }
    errors.push(SyntaxError::new(
        "`let` expressions are not supported here",
        let_.syntax().text_range(),
    ));
}
<|endoftext|>