<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime};

use log::warn;
use rrg_proto::{GetFileStatRequest, path_spec::Options, path_spec::PathType, PathSpec, StatEntry};

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Response {
    mode: u64,
    inode: u64,
    device: u64,
    hard_links: u64,
    uid: u32,
    gid: u32,
    size: u64,
    access_time: Option<SystemTime>,
    modification_time: Option<SystemTime>,
    status_change_time: Option<SystemTime>,
    block_count: u64,
    block_size: u64,
    represented_device: u64,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    symlink: Option<PathBuf>,
    path: PathBuf,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let destination = if request.follow_symlink {
        fs::canonicalize(&request.path)?
    } else {
        request.path.clone()
    };

    let mut response = form_response(&request.path, &destination)?;

    if request.collect_ext_attrs {
        // TODO: This is not pretty. Consider creating a blank `ext_attrs`
        // implementation for Windows and make this code compile regardless of
        // the platform.
        #[cfg(target_family = "unix")]
        {
            response.ext_attrs = crate::fs::unix::ext_attrs(&destination)?.collect();
        }
    }

    session.reply(response)?;
    Ok(())
}

#[cfg(target_os = "linux")]
fn get_time_option<E: std::fmt::Display>(time: Result<SystemTime, E>) -> Option<SystemTime> {
    match time {
        Ok(time_value) => Some(time_value),
        Err(err) => {
            warn!("Unable to get time value: {}", err);
            None
        }
    }
}

fn get_time_since_unix_epoch(sys_time: &Option<SystemTime>) -> Option<u64> {
    match sys_time {
        Some(time_value) => match rrg_proto::micros(*time_value) {
            Ok(micros_value) => Some(micros_value),
            Err(error) => {
                warn!("failed to convert time: {}", error);
                None
            }
        }
        None => None
    }
}

#[cfg(target_os = "linux")]
fn get_status_change_time(metadata: &fs::Metadata) -> Option<SystemTime> {
    use std::time::Duration;
    use std::os::unix::fs::MetadataExt;
    use std::time::UNIX_EPOCH;

    UNIX_EPOCH.checked_add(Duration::from_secs(metadata.ctime() as u64))
}

#[cfg(target_os = "linux")]
fn form_response(original_path: &Path, destination: &Path)
                 -> Result<Response, std::io::Error> {
    use std::os::unix::fs::MetadataExt;

    let metadata = fs::symlink_metadata(destination)?;
    let original_metadata = fs::symlink_metadata(original_path)?;

    Ok(Response {
        mode: metadata.mode() as u64,
        inode: metadata.ino(),
        device: metadata.dev(),
        hard_links: metadata.nlink(),
        uid: metadata.uid() as u32,
        gid: metadata.gid() as u32,
        size: metadata.size(),
        access_time: get_time_option(metadata.accessed()),
        modification_time: get_time_option(metadata.modified()),
        status_change_time: get_status_change_time(&metadata),
        block_count: metadata.blocks(),
        block_size: metadata.blksize(),
        represented_device: metadata.rdev(),

        #[cfg(target_os = "linux")]
        flags_linux: crate::fs::linux::flags(destination).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to collect flags for '{path}': {cause}",
                path = destination.display(),
                cause = error,
            }
        }).ok(),

        symlink: match original_metadata.file_type().is_symlink() {
            true => Some(fs::read_link(original_path).unwrap()),
            false => None
        },

        path: original_path.to_owned(),

        ext_attrs: vec![],
    })
}

#[cfg(not(target_os = "linux"))]
fn form_response(_original_path: &PathBuf, _destination: &PathBuf)
                 -> Result<Response, session::Error> {
    Err(session::Error::Dispatch(
        String::from("This functionality has not yet been implemented for your platform.")))
}

fn collapse_pathspec(pathspec: PathSpec) -> PathBuf {
    fn recursive_collapse(pathspec: PathSpec) -> PathBuf {
        match pathspec.path {
            Some(path) => {
                let path_buf = PathBuf::from(path);
                match pathspec.nested_path {
                    Some(nested_path_box) => path_buf.join(recursive_collapse(*nested_path_box)),
                    None => path_buf,
                }
            }
            None => PathBuf::default()
        }
    }

    let mut result = recursive_collapse(pathspec);
    if !result.has_root() {
        result = PathBuf::from("/").join(result);
    }
    result
}

impl super::Request for Request {

    type Proto = GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        match proto.pathspec {
            Some(pathspec) => Ok(Request {
                path: collapse_pathspec(pathspec),
                collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
                follow_symlink: proto.follow_symlink.unwrap_or(false),
            }),

            None => Err(session::ParseError::from(
                session::MissingFieldError::new("path spec"))),
        }
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = StatEntry;

    fn into_proto(self) -> Self::Proto {
        StatEntry {
            st_mode: Some(self.mode),
            st_ino: Some(self.inode),
            st_dev: Some(self.device),
            st_nlink: Some(self.hard_links),
            st_uid: Some(self.uid),
            st_gid: Some(self.gid),
            st_size: Some(self.size),
            st_atime: get_time_since_unix_epoch(&self.access_time),
            st_mtime: get_time_since_unix_epoch(&self.modification_time),
            st_ctime: get_time_since_unix_epoch(&self.status_change_time),
            st_blocks: Some(self.block_count),
            st_blksize: Some(self.block_size),
            st_rdev: Some(self.represented_device),
            st_flags_osx: None,
            st_flags_linux: self.flags_linux,

            symlink: match self.symlink {
                Some(path) => Some(path.to_str().unwrap().to_string()),
                None => None
            },

            registry_type: None,
            resident: None,

            pathspec: Some(PathSpec {
                path_options: Some(Options::CaseLiteral as i32),
                pathtype: Some(PathType::Os as i32),
                path: Some(self.path.to_str().unwrap().to_string()),
                ..Default::default()
            }),

            registry_data: None,
            st_btime: None,
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::tempdir;

    use crate::action::Request;

    use super::*;

    #[test]
    fn test_path_collapse() {
        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("file")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("to")),
                    ..Default::default()
                })),
            path: Some(String::from("path")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/file"));

        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("on/device")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("some/file")),
                    ..Default::default()
                })),
            path: Some(String::from("path/to")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/some/file/on/device"));
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_empty_request() {
        let request: Result<super::Request, _> =
            Request::from_proto(GetFileStatRequest::default());
        assert!(request.is_err());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_no_error_with_existing_file() {
        let dir = tempdir().unwrap();

        let file_path = dir.path().join("temp_file.txt");
        fs::File::create(file_path.to_path_buf()).unwrap();
        let response = form_response(&file_path, &file_path);
        assert!(response.is_ok());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_file_does_not_exist() {
        let dir = tempdir().unwrap();

        let file_path = dir.path().join("temp_file.txt");
        let response = form_response(&file_path, &file_path);
        assert!(response.is_err());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_mode_and_size() {
        let new_size = 42;

        let dir = tempdir().unwrap();
        let file_path = dir.path().join("temp_file.txt");
        let file = fs::File::create(file_path.to_path_buf()).unwrap();

        file.set_len(new_size).unwrap();
        let mut permissions = fs::metadata(&file_path).unwrap().permissions();
        permissions.set_readonly(true);
        file.set_permissions(permissions).unwrap();

        let response = form_response(&file_path, &file_path);

        assert!(response.is_ok());

        let response = response.unwrap();
        assert_eq!(response.size, new_size);
        // We check only user permissions since others might not be set.
        assert_eq!(response.mode & 0o700, 0o400);
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_hard_link() {
        let dir = tempdir().unwrap();
        let file_path = dir.path().join("temp_file.txt");
        let hard_link_path = dir.path().join("hard_link.txt");
        fs::File::create(file_path.to_path_buf()).unwrap();
        fs::hard_link(&file_path, &hard_link_path).unwrap();

        let file_response = form_response(&file_path, &file_path);
        let link_response = form_response(&hard_link_path, &hard_link_path);

        assert!(file_response.is_ok());
        assert!(link_response.is_ok());

        let file_response = file_response.unwrap();
        let link_response = link_response.unwrap();

        assert_eq!(file_response.hard_links, 2);
        assert_eq!(link_response.hard_links, 2);
        assert_eq!(file_response.inode, link_response.inode);
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_chain_of_symlinks() {
        use std::os::unix;

        let dir = tempdir().unwrap();
        let file_path = dir.path().join("temp_file.txt");
        fs::File::create(file_path.to_path_buf()).unwrap();

        let chain_length = 5;
        unix::fs::symlink(&file_path, dir.path().join("symlink 0")).unwrap();

        for i in 1..chain_length {
            unix::fs::symlink(dir.path().join(format!("symlink {}", i - 1)),
                              dir.path().join(format!("symlink {}", i))).unwrap();
        }

        let last_symlink = dir.path().join(format!("symlink {}", chain_length - 1));
        let previous_symlink = dir.path().join(format!("symlink {}", chain_length - 2));

        let response = form_response(&last_symlink,
                                     &fs::canonicalize(&last_symlink).unwrap());
        let original_response = form_response(&file_path, &file_path);

        assert!(response.is_ok());
        let response = response.unwrap();

        assert!(original_response.is_ok());
        let original_response = original_response.unwrap();

        assert!(response.symlink.is_some());
        assert!(original_response.symlink.is_none());
        assert_eq!(response.symlink.unwrap(), previous_symlink);

        assert_eq!(response.mode, original_response.mode);
        assert_eq!(response.inode, original_response.inode);
        assert_eq!(response.device, original_response.device);
        assert_eq!(response.hard_links, original_response.hard_links);
        assert_eq!(response.uid, original_response.uid);
        assert_eq!(response.gid, original_response.gid);
        assert_eq!(response.size, original_response.size);
        assert_eq!(response.access_time, original_response.access_time);
        assert_eq!(response.modification_time, original_response.modification_time);
        assert_eq!(response.status_change_time, original_response.status_change_time);
        assert_eq!(response.block_count, original_response.block_count);
        assert_eq!(response.block_size, original_response.block_size);
        assert_eq!(response.represented_device, original_response.represented_device);
        assert_eq!(response.flags_linux, original_response.flags_linux);
    }
}
<commit_msg>Refactor implementation of the stat action.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action responses with stat of a given file

use std::fs;
use std::fs::Metadata;
use std::path::{Path, PathBuf};
use std::time::{SystemTime};

use log::warn;
use rrg_proto::{GetFileStatRequest, path_spec::Options, path_spec::PathType, PathSpec, StatEntry};

use crate::session::{self, Error, Session};

impl From<std::io::Error> for Error {

    fn from(e: std::io::Error) -> Error {
        Error::action(e)
    }
}

#[derive(Debug)]
pub struct Response {
    metadata: Metadata,
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    symlink: Option<PathBuf>,
    path: PathBuf,
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
}

#[derive(Debug)]
pub struct Request {
    path: PathBuf,
    collect_ext_attrs: bool,
    follow_symlink: bool,
}

pub fn handle<S: Session>(session: &mut S, request: Request) -> session::Result<()> {
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)?
    } else {
        std::fs::symlink_metadata(&request.path)?
    };

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let mut response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        ext_attrs: vec!(),
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    if request.collect_ext_attrs {
        // TODO: This is not pretty. Consider creating a blank `ext_attrs`
        // implementation for Windows and make this code compile regardless of
        // the platform.
        #[cfg(target_family = "unix")]
        {
            // TODO: Do not fail on error.
            response.ext_attrs.extend(crate::fs::unix::ext_attrs(&response.path)?);
        }
    }

    session.reply(response)?;
    Ok(())
}

#[cfg(target_os = "linux")]
fn get_time_option<E: std::fmt::Display>(time: Result<SystemTime, E>) -> Option<SystemTime> {
    match time {
        Ok(time_value) => Some(time_value),
        Err(err) => {
            warn!("Unable to get time value: {}", err);
            None
        }
    }
}

fn get_time_since_unix_epoch(sys_time: &Option<SystemTime>) -> Option<u64> {
    match sys_time {
        Some(time_value) => match rrg_proto::micros(*time_value) {
            Ok(micros_value) => Some(micros_value),
            Err(error) => {
                warn!("failed to convert time: {}", error);
                None
            }
        }
        None => None
    }
}

#[cfg(target_os = "linux")]
fn get_status_change_time(metadata: &fs::Metadata) -> Option<SystemTime> {
    use std::time::Duration;
    use std::os::unix::fs::MetadataExt;
    use std::time::UNIX_EPOCH;

    UNIX_EPOCH.checked_add(Duration::from_secs(metadata.ctime() as u64))
}

fn collapse_pathspec(pathspec: PathSpec) -> PathBuf {
    fn recursive_collapse(pathspec: PathSpec) -> PathBuf {
        match pathspec.path {
            Some(path) => {
                let path_buf = PathBuf::from(path);
                match pathspec.nested_path {
                    Some(nested_path_box) => path_buf.join(recursive_collapse(*nested_path_box)),
                    None => path_buf,
                }
            }
            None => PathBuf::default()
        }
    }

    let mut result = recursive_collapse(pathspec);
    if !result.has_root() {
        result = PathBuf::from("/").join(result);
    }
    result
}

impl super::Request for Request {

    type Proto = GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        match proto.pathspec {
            Some(pathspec) => Ok(Request {
                path: collapse_pathspec(pathspec),
                collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
                follow_symlink: proto.follow_symlink.unwrap_or(false),
            }),

            None => Err(session::ParseError::from(
                session::MissingFieldError::new("path spec"))),
        }
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            st_flags_linux: self.flags_linux,
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::tempdir;

    use crate::action::Request;

    use super::*;

    #[test]
    fn test_path_collapse() {
        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("file")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("to")),
                    ..Default::default()
                })),
            path: Some(String::from("path")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/file"));

        let pathspec = PathSpec {
            nested_path: Some(Box::new(
                PathSpec {
                    nested_path: Some(Box::new(
                        PathSpec {
                            path: Some(String::from("on/device")),
                            ..Default::default()
                        }
                    )),
                    path: Some(String::from("some/file")),
                    ..Default::default()
                })),
            path: Some(String::from("path/to")),
            ..Default::default()
        };

        assert_eq!(collapse_pathspec(pathspec), PathBuf::from("/path/to/some/file/on/device"));
    }
}
<|endoftext|>


<commit_before>extern crate html2text;
extern crate termion;
extern crate argparse;
use std::io::{self, Write};
use html2text::render::text_renderer::{RichAnnotation};
use argparse::{ArgumentParser, Store};
use termion::input::TermRead;
use termion::event::Key;
use termion::raw::IntoRawMode;
use termion::cursor::Goto;

fn to_style(tag: &Vec<RichAnnotation>) -> String {
    let mut style = String::new();

    for ann in tag {
        match *ann {
            RichAnnotation::Default => (),
            RichAnnotation::Link(_) => {
                style.push_str(&format!("{}", termion::style::Underline));
            },
            RichAnnotation::Image => {
                style.push_str(&format!("{}", termion::color::Fg(termion::color::LightBlue)));
            },
        }
    }
    style
}

fn main() {
    let mut filename = String::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut filename)
          .add_argument("filename", Store, "Set HTML filename");
        ap.parse_args_or_exit();
    }

    let (width, height) = termion::terminal_size().unwrap();
    let (width, height) = (width as usize, height as usize);

    let mut file = std::fs::File::open(filename).expect("Tried to open file");
    let annotated = html2text::from_read_rich(&mut file, width as usize);

    let mut keys = io::stdin().keys();
    let mut stdout = io::stdout().into_raw_mode().unwrap();
    let mut pos = 0;
    let mut cursor_x = 1;
    let mut cursor_y = 1;
    loop {
        let maxpos = std::cmp::min(pos+height, annotated.len());
        write!(stdout, "{}", termion::clear::All).unwrap();
        for (i, line) in annotated[pos..maxpos].iter().enumerate() {
            write!(stdout, "{}", Goto(1, i as u16 +1)).unwrap();
            for (s, tag) in line.iter() {
                let style = to_style(tag);
                write!(stdout, "{}{}{}", style, s, termion::style::Reset).unwrap();
            }
        }
        write!(stdout, "{}", Goto(cursor_x, cursor_y)).unwrap();
        stdout.flush().unwrap();
        if let Some(Ok(k)) = keys.next() {
            match k {
                Key::Char('q') => break,
                Key::Char('j') => {
                    if cursor_y < height as u16 {
                        cursor_y += 1;
                    }
                },
                Key::Char('k') => {
                    if cursor_y > 1 {
                        cursor_y -= 1;
                    }
                },
                Key::Char('h') => {
                    if cursor_x > 1 {
                        cursor_x -= 1;
                    }
                },
                Key::Char('l') => {
                    if cursor_x < width as u16 {
                        cursor_x += 1;
                    }
                },
                Key::Char(' ') | Key::PageDown => {
                    if pos + height-1 < annotated.len() {
                        pos += height-1;
                    }
                },
                Key::PageUp => {
                    if pos >= height-1 {
                        pos -= height-1;
                    }
                },
                _ => {},
            }
        }
    }
}<commit_msg>Add cursor keys.<commit_after>extern crate html2text;
extern crate termion;
extern crate argparse;
use std::io::{self, Write};
use html2text::render::text_renderer::{RichAnnotation};
use argparse::{ArgumentParser, Store};
use termion::input::TermRead;
use termion::event::Key;
use termion::raw::IntoRawMode;
use termion::cursor::Goto;

fn to_style(tag: &Vec<RichAnnotation>) -> String {
    let mut style = String::new();

    for ann in tag {
        match *ann {
            RichAnnotation::Default => (),
            RichAnnotation::Link(_) => {
                style.push_str(&format!("{}", termion::style::Underline));
            },
            RichAnnotation::Image => {
                style.push_str(&format!("{}", termion::color::Fg(termion::color::LightBlue)));
            },
        }
    }
    style
}

fn main() {
    let mut filename = String::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut filename)
          .add_argument("filename", Store, "Set HTML filename");
        ap.parse_args_or_exit();
    }

    let (width, height) = termion::terminal_size().unwrap();
    let (width, height) = (width as usize, height as usize);

    let mut file = std::fs::File::open(filename).expect("Tried to open file");
    let annotated = html2text::from_read_rich(&mut file, width as usize);

    let mut keys = io::stdin().keys();
    let mut stdout = io::stdout().into_raw_mode().unwrap();
    let mut pos = 0;
    let mut cursor_x = 1;
    let mut cursor_y = 1;
    loop {
        let maxpos = std::cmp::min(pos+height, annotated.len());
        write!(stdout, "{}", termion::clear::All).unwrap();
        for (i, line) in annotated[pos..maxpos].iter().enumerate() {
            write!(stdout, "{}", Goto(1, i as u16 +1)).unwrap();
            for (s, tag) in line.iter() {
                let style = to_style(tag);
                write!(stdout, "{}{}{}", style, s, termion::style::Reset).unwrap();
            }
        }
        write!(stdout, "{}", Goto(cursor_x, cursor_y)).unwrap();
        stdout.flush().unwrap();
        if let Some(Ok(k)) = keys.next() {
            match k {
                Key::Char('q') => break,
                Key::Char('j') | Key::Down => {
                    if cursor_y < height as u16 {
                        cursor_y += 1;
                    }
                },
                Key::Char('k') | Key::Up => {
                    if cursor_y > 1 {
                        cursor_y -= 1;
                    }
                },
                Key::Char('h') | Key::Left => {
                    if cursor_x > 1 {
                        cursor_x -= 1;
                    }
                },
                Key::Char('l') | Key::Right => {
                    if cursor_x < width as u16 {
                        cursor_x += 1;
                    }
                },
                Key::Char(' ') | Key::PageDown => {
                    if pos + height-1 < annotated.len() {
                        pos += height-1;
                    }
                },
                Key::PageUp => {
                    if pos >= height-1 {
                        pos -= height-1;
                    }
                },
                _ => {},
            }
        }
    }
}<|endoftext|>


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

use crate::{
    block::Block,
    common::{Author, Round},
    sync_info::SyncInfo,
};
use anyhow::{ensure, format_err, Context, Result};
use libra_types::validator_verifier::ValidatorVerifier;
use serde::{Deserialize, Serialize};
use std::fmt;

/// ProposalMsg contains the required information for the proposer election protocol to make its
/// choice (typically depends on round and proposer info).
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ProposalMsg {
    proposal: Block,
    sync_info: SyncInfo,
}

impl ProposalMsg {
    /// Creates a new proposal.
    pub fn new(proposal: Block, sync_info: SyncInfo) -> Self {
        Self {
            proposal,
            sync_info,
        }
    }

    pub fn epoch(&self) -> u64 {
        self.proposal.epoch()
    }

    /// Verifies that the ProposalMsg is well-formed.
    pub fn verify_well_formed(&self) -> Result<()> {
        ensure!(
            !self.proposal.is_nil_block(),
            "Proposal {} for a NIL block",
            self.proposal
        );
        self.proposal
            .verify_well_formed()
            .context("Fail to verify ProposalMsg's block")?;
        ensure!(
            self.proposal.round() > 0,
            "Proposal for {} has an incorrect round of 0",
            self.proposal,
        );
        ensure!(
            self.proposal.epoch() == self.sync_info.epoch(),
            "ProposalMsg has different epoch number from SyncInfo"
        );
        ensure!(
            self.proposal.parent_id()
                == self.sync_info.highest_quorum_cert().certified_block().id(),
            "Proposal HQC in SyncInfo certifies {}, but block parent id is {}",
            self.sync_info.highest_quorum_cert().certified_block().id(),
            self.proposal.parent_id(),
        );
        let previous_round = self.proposal.round() - 1;
        let highest_certified_round = std::cmp::max(
            self.proposal.quorum_cert().certified_block().round(),
            self.sync_info
                .highest_timeout_certificate()
                .map_or(0, |tc| tc.round()),
        );
        ensure!(
            previous_round == highest_certified_round,
            "Proposal {} does not have a certified round {}",
            self.proposal,
            previous_round
        );
        ensure!(
            self.proposal.author().is_some(),
            "Proposal {} does not define an author",
            self.proposal
        );
        Ok(())
    }

    pub fn verify(&self, validator: &ValidatorVerifier) -> Result<()> {
        self.proposal
            .validate_signatures(validator)
            .map_err(|e| format_err!("{:?}", e))?;
        // if there is a timeout certificate, verify its signatures
        if let Some(tc) = self.sync_info.highest_timeout_certificate() {
            tc.verify(validator).map_err(|e| format_err!("{:?}", e))?;
        }
        // Note that we postpone the verification of SyncInfo until it's being used.
        self.verify_well_formed()
    }

    pub fn proposal(&self) -> &Block {
        &self.proposal
    }

    pub fn take_proposal(self) -> Block {
        self.proposal
    }

    pub fn sync_info(&self) -> &SyncInfo {
        &self.sync_info
    }

    pub fn round(&self) -> Round {
        self.proposal.round()
    }

    pub fn proposer(&self) -> Author {
        self.proposal
            .author()
            .expect("Proposal should be verified having an author")
    }
}

impl fmt::Display for ProposalMsg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let author = match self.proposal.author() {
            Some(author) => author.short_str(),
            None => String::from("NIL"),
        };
        write!(f, "[proposal {} from {}]", self.proposal, author,)
    }
}
<commit_msg>Removing round from consensus/consensus-types/src/proposal_msg.rs<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{block::Block, common::Author, sync_info::SyncInfo};
use anyhow::{ensure, format_err, Context, Result};
use libra_types::validator_verifier::ValidatorVerifier;
use serde::{Deserialize, Serialize};
use std::fmt;

/// ProposalMsg contains the required information for the proposer election protocol to make its
/// choice (typically depends on round and proposer info).
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ProposalMsg {
    proposal: Block,
    sync_info: SyncInfo,
}

impl ProposalMsg {
    /// Creates a new proposal.
    pub fn new(proposal: Block, sync_info: SyncInfo) -> Self {
        Self {
            proposal,
            sync_info,
        }
    }

    pub fn epoch(&self) -> u64 {
        self.proposal.epoch()
    }

    /// Verifies that the ProposalMsg is well-formed.
    pub fn verify_well_formed(&self) -> Result<()> {
        ensure!(
            !self.proposal.is_nil_block(),
            "Proposal {} for a NIL block",
            self.proposal
        );
        self.proposal
            .verify_well_formed()
            .context("Fail to verify ProposalMsg's block")?;
        ensure!(
            self.proposal.round() > 0,
            "Proposal for {} has an incorrect round of 0",
            self.proposal,
        );
        ensure!(
            self.proposal.epoch() == self.sync_info.epoch(),
            "ProposalMsg has different epoch number from SyncInfo"
        );
        ensure!(
            self.proposal.parent_id()
                == self.sync_info.highest_quorum_cert().certified_block().id(),
            "Proposal HQC in SyncInfo certifies {}, but block parent id is {}",
            self.sync_info.highest_quorum_cert().certified_block().id(),
            self.proposal.parent_id(),
        );
        let previous_round = self.proposal.round() - 1;
        let highest_certified_round = std::cmp::max(
            self.proposal.quorum_cert().certified_block().round(),
            self.sync_info
                .highest_timeout_certificate()
                .map_or(0, |tc| tc.round()),
        );
        ensure!(
            previous_round == highest_certified_round,
            "Proposal {} does not have a certified round {}",
            self.proposal,
            previous_round
        );
        ensure!(
            self.proposal.author().is_some(),
            "Proposal {} does not define an author",
            self.proposal
        );
        Ok(())
    }

    pub fn verify(&self, validator: &ValidatorVerifier) -> Result<()> {
        self.proposal
            .validate_signatures(validator)
            .map_err(|e| format_err!("{:?}", e))?;
        // if there is a timeout certificate, verify its signatures
        if let Some(tc) = self.sync_info.highest_timeout_certificate() {
            tc.verify(validator).map_err(|e| format_err!("{:?}", e))?;
        }
        // Note that we postpone the verification of SyncInfo until it's being used.
        self.verify_well_formed()
    }

    pub fn proposal(&self) -> &Block {
        &self.proposal
    }

    pub fn take_proposal(self) -> Block {
        self.proposal
    }

    pub fn sync_info(&self) -> &SyncInfo {
        &self.sync_info
    }

    pub fn proposer(&self) -> Author {
        self.proposal
            .author()
            .expect("Proposal should be verified having an author")
    }
}

impl fmt::Display for ProposalMsg {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let author = match self.proposal.author() {
            Some(author) => author.short_str(),
            None => String::from("NIL"),
        };
        write!(f, "[proposal {} from {}]", self.proposal, author,)
    }
}
<|endoftext|>


<commit_before>extern crate termion;
extern crate tui;

use std::io;
use termion::event;
use termion::input::TermRead;

use tui::Terminal;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Style};
use tui::widgets::{Block, Paragraph, Widget};

fn main() {
    let mut terminal = Terminal::new(MouseBackend::new().unwrap()).unwrap();
    let stdin = io::stdin();
    terminal.clear().unwrap();
    terminal.hide_cursor().unwrap();

    let mut term_size = terminal.size().unwrap();
    draw(&mut terminal, &term_size);

    for c in stdin.keys() {
        let size = terminal.size().unwrap();
        if size != term_size {
            terminal.resize(size).unwrap();
            term_size = size;
        }

        draw(&mut terminal, &term_size);
        let evt = c.unwrap();
        if evt == event::Key::Char('q') {
            break;
        }
    }
    terminal.show_cursor().unwrap();
}

fn draw(t: &mut Terminal<MouseBackend>, size: &Rect) {
    Block::default()
        .style(Style::default().bg(Color::White))
        .render(t, size);

    Group::default()
        .direction(Direction::Vertical)
        .margin(5)
        .sizes(&[Size::Percent(100)])
        .render(t, size, |t, chunks| {
            Group::default()
                .direction(Direction::Horizontal)
                .sizes(&[Size::Percent(100)])
                .render(t, &chunks[0], |t, chunks| {
                    Paragraph::default()
                        .text(
                            "This is a line\n{fg=red This is a line}\n{bg=red This is a \
                             line}\n{mod=italic This is a line}\n{mod=bold This is a \
                             line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
                             line}\n{mod=underline This is a \
                             line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
                        )
                        .render(t, &chunks[0]);
                });
        });

    t.draw().unwrap();
}
<commit_msg>[examples] update paragraph example to show text alignment<commit_after>extern crate termion;
extern crate tui;

use std::io;
use termion::event;
use termion::input::TermRead;

use tui::Terminal;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Alignment, Color, Style};
use tui::widgets::{Block, Paragraph, Widget};

fn main() {
    let mut terminal = Terminal::new(MouseBackend::new().unwrap()).unwrap();
    let stdin = io::stdin();
    terminal.clear().unwrap();
    terminal.hide_cursor().unwrap();

    let mut term_size = terminal.size().unwrap();
    draw(&mut terminal, &term_size);

    for c in stdin.keys() {
        let size = terminal.size().unwrap();
        if size != term_size {
            terminal.resize(size).unwrap();
            term_size = size;
        }

        draw(&mut terminal, &term_size);
        let evt = c.unwrap();
        if evt == event::Key::Char('q') {
            break;
        }
    }
    terminal.show_cursor().unwrap();
}

fn draw(t: &mut Terminal<MouseBackend>, size: &Rect) {
    Block::default()
        .style(Style::default().bg(Color::White))
        .render(t, size);

    Group::default()
        .direction(Direction::Vertical)
        .margin(5)
        .sizes(&[Size::Percent(30), Size::Percent(30), Size::Percent(30)])
        .render(t, size, |t, chunks| {
            Group::default()
                .direction(Direction::Horizontal)
                .sizes(&[Size::Percent(100)])
                .render(t, &chunks[0], |t, chunks| {
                    Paragraph::default()
                        .alignment(Alignment::Left)
                        .text(
                            "This is a line\n{fg=red This is a line}\n{bg=red This is a \
                             line}\n{mod=italic This is a line}\n{mod=bold This is a \
                             line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
                             line}\n{mod=underline This is a \
                             line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
                        )
                        .render(t, &chunks[0]);
                });
            Group::default()
                .direction(Direction::Horizontal)
                .sizes(&[Size::Percent(100)])
                .render(t, &chunks[1], |t, chunks| {
                    Paragraph::default()
                        .alignment(Alignment::Center)
                        .wrap(true)
                        .text(
                            "This is a line\n{fg=red This is a line}\n{bg=red This is a \
                             line}\n{mod=italic This is a line}\n{mod=bold This is a \
                             line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
                             line}\n{mod=underline This is a \
                             line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
                        )
                        .render(t, &chunks[0]);
                });
            Group::default()
                .direction(Direction::Horizontal)
                .sizes(&[Size::Percent(100)])
                .render(t, &chunks[2], |t, chunks| {
                    Paragraph::default()
                        .alignment(Alignment::Right)
                        .wrap(true)
                        .text(
                            "This is a line\n{fg=red This is a line}\n{bg=red This is a \
                             line}\n{mod=italic This is a line}\n{mod=bold This is a \
                             line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
                             line}\n{mod=underline This is a \
                             line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
                        )
                        .render(t, &chunks[0]);
                });
        });

    t.draw().unwrap();
}
<|endoftext|>


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

//! Project and package linters that run queries on guppy.

use crate::config::EnforcedAttributesConfig;
use std::collections::HashMap;
use x_lint::prelude::*;

/// Ban certain crates from being used as direct dependencies.
#[derive(Debug)]
pub struct BannedDirectDeps<'cfg> {
    banned_deps: &'cfg HashMap<String, String>,
}

impl<'cfg> BannedDirectDeps<'cfg> {
    pub fn new(banned_deps: &'cfg HashMap<String, String>) -> Self {
        Self { banned_deps }
    }
}

impl<'cfg> Linter for BannedDirectDeps<'cfg> {
    fn name(&self) -> &'static str {
        "banned-direct-deps"
    }
}

// This could be done either as a project linter or as a package linter -- doing it as a project
// linter is slightly cheaper empirically.
impl<'cfg> ProjectLinter for BannedDirectDeps<'cfg> {
    fn run<'l>(
        &self,
        ctx: &ProjectContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let package_graph = ctx.package_graph()?;
        let banned_packages = package_graph.packages().filter_map(|package| {
            self.banned_deps
                .get(package.name())
                .map(|message| (package, message))
        });

        for (package, message) in banned_packages {
            // Look at the reverse direct dependencies of this package.
            let dep_links = package_graph
                .reverse_dep_links(package.id())
                .expect("valid package ID");
            for link in dep_links {
                if let Some(workspace_path) = link.from.workspace_path() {
                    out.write_kind(
                        LintKind::Package {
                            name: link.from.name(),
                            workspace_path,
                        },
                        LintLevel::Error,
                        format!("banned direct dependency '{}': {}", package.name(), message),
                    );
                }
            }
        }

        Ok(RunStatus::Executed)
    }
}

/// Enforce attributes on workspace crates.
#[derive(Debug)]
pub struct EnforcedAttributes<'cfg> {
    config: &'cfg EnforcedAttributesConfig,
}

impl<'cfg> EnforcedAttributes<'cfg> {
    pub fn new(config: &'cfg EnforcedAttributesConfig) -> Self {
        Self { config }
    }
}

impl<'cfg> Linter for EnforcedAttributes<'cfg> {
    fn name(&self) -> &'static str {
        "enforced-attributes"
    }
}

impl<'cfg> PackageLinter for EnforcedAttributes<'cfg> {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let metadata = ctx.metadata();
        if let Some(authors) = &self.config.authors {
            if metadata.authors() != authors.as_slice() {
                out.write(
                    LintLevel::Error,
                    format!("invalid authors (expected {:?})", authors.join(", "),),
                );
            }
        }
        if let Some(license) = &self.config.license {
            if metadata.license() != Some(license.as_str()) {
                out.write(
                    LintLevel::Error,
                    format!("invalid license (expected {})", license),
                )
            }
        }

        Ok(RunStatus::Executed)
    }
}

/// Check conventions in crate names and paths.
#[derive(Debug)]
pub struct CrateNamesPaths;

impl Linter for CrateNamesPaths {
    fn name(&self) -> &'static str {
        "crate-names-paths"
    }
}

impl PackageLinter for CrateNamesPaths {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let name = ctx.metadata().name();
        if name.contains('_') {
            out.write(
                LintLevel::Error,
                "crate name contains '_' (use '-' instead)",
            );
        }
        let workspace_path = ctx.workspace_path();
        if let Some(path) = workspace_path.to_str() {
            if path.contains('_') {
                out.write(
                    LintLevel::Error,
                    "workspace path contains '_' (use '-' instead)",
                );
            }
        } else {
            // Workspace path is invalid UTF-8. A different lint should catch this.
        }
        Ok(RunStatus::Executed)
    }
}

/// Ensure libra-workspace-hack is a dependency
#[derive(Debug)]
pub struct WorkspaceHack;

impl Linter for WorkspaceHack {
    fn name(&self) -> &'static str {
        "workspace-hack"
    }
}

impl PackageLinter for WorkspaceHack {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let package_id = ctx.metadata().id();
        let pkg_graph = ctx
            .project_ctx()
            .package_graph()
            .expect("can't find package graph");
        let workspace_hack_id = pkg_graph
            .workspace()
            .member_by_name("libra-workspace-hack")
            .expect("can't find libra-workspace-hack package")
            .id();

        // libra-workspace-hack does not need to depend on itself
        if package_id == workspace_hack_id {
            return Ok(RunStatus::Executed);
        }

        let has_links = pkg_graph
            .dep_links(package_id)
            .expect("valid package ID")
            .next()
            .is_some();
        let has_hack_dep = pkg_graph
            .directly_depends_on(package_id, workspace_hack_id)
            .expect("valid package ID");
        if has_links && !has_hack_dep {
            out.write(LintLevel::Error, "missing libra-workspace-hack dependency");
        }

        Ok(RunStatus::Executed)
    }
}
<commit_msg>[x] update guppy and add check for build target names<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Project and package linters that run queries on guppy.

use crate::config::EnforcedAttributesConfig;
use std::{collections::HashMap, ffi::OsStr};
use x_lint::prelude::*;

/// Ban certain crates from being used as direct dependencies.
#[derive(Debug)]
pub struct BannedDirectDeps<'cfg> {
    banned_deps: &'cfg HashMap<String, String>,
}

impl<'cfg> BannedDirectDeps<'cfg> {
    pub fn new(banned_deps: &'cfg HashMap<String, String>) -> Self {
        Self { banned_deps }
    }
}

impl<'cfg> Linter for BannedDirectDeps<'cfg> {
    fn name(&self) -> &'static str {
        "banned-direct-deps"
    }
}

// This could be done either as a project linter or as a package linter -- doing it as a project
// linter is slightly cheaper empirically.
impl<'cfg> ProjectLinter for BannedDirectDeps<'cfg> {
    fn run<'l>(
        &self,
        ctx: &ProjectContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let package_graph = ctx.package_graph()?;
        let banned_packages = package_graph.packages().filter_map(|package| {
            self.banned_deps
                .get(package.name())
                .map(|message| (package, message))
        });

        for (package, message) in banned_packages {
            // Look at the reverse direct dependencies of this package.
            let dep_links = package_graph
                .reverse_dep_links(package.id())
                .expect("valid package ID");
            for link in dep_links {
                if let Some(workspace_path) = link.from.workspace_path() {
                    out.write_kind(
                        LintKind::Package {
                            name: link.from.name(),
                            workspace_path,
                        },
                        LintLevel::Error,
                        format!("banned direct dependency '{}': {}", package.name(), message),
                    );
                }
            }
        }

        Ok(RunStatus::Executed)
    }
}

/// Enforce attributes on workspace crates.
#[derive(Debug)]
pub struct EnforcedAttributes<'cfg> {
    config: &'cfg EnforcedAttributesConfig,
}

impl<'cfg> EnforcedAttributes<'cfg> {
    pub fn new(config: &'cfg EnforcedAttributesConfig) -> Self {
        Self { config }
    }
}

impl<'cfg> Linter for EnforcedAttributes<'cfg> {
    fn name(&self) -> &'static str {
        "enforced-attributes"
    }
}

impl<'cfg> PackageLinter for EnforcedAttributes<'cfg> {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let metadata = ctx.metadata();
        if let Some(authors) = &self.config.authors {
            if metadata.authors() != authors.as_slice() {
                out.write(
                    LintLevel::Error,
                    format!("invalid authors (expected {:?})", authors.join(", "),),
                );
            }
        }
        if let Some(license) = &self.config.license {
            if metadata.license() != Some(license.as_str()) {
                out.write(
                    LintLevel::Error,
                    format!("invalid license (expected {})", license),
                )
            }
        }

        Ok(RunStatus::Executed)
    }
}

/// Check conventions in crate names and paths.
#[derive(Debug)]
pub struct CrateNamesPaths;

impl Linter for CrateNamesPaths {
    fn name(&self) -> &'static str {
        "crate-names-paths"
    }
}

impl PackageLinter for CrateNamesPaths {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let name = ctx.metadata().name();
        if name.contains('_') {
            out.write(
                LintLevel::Error,
                "crate name contains '_' (use '-' instead)",
            );
        }

        let workspace_path = ctx.workspace_path();
        if let Some(path) = workspace_path.to_str() {
            if path.contains('_') {
                out.write(
                    LintLevel::Error,
                    "workspace path contains '_' (use '-' instead)",
                );
            }
        } else {
            // Workspace path is invalid UTF-8. A different lint should catch this.
        }

        for build_target in ctx.metadata().build_targets() {
            let target_name = build_target.name();
            if target_name.contains('_') {
                // If the path is implicitly specified by the name, don't warn about it.
                let file_stem = build_target.path().file_stem();
                if file_stem != Some(OsStr::new(target_name)) {
                    out.write(
                        LintLevel::Error,
                        format!(
                            "build target '{}' contains '_' (use '-' instead)",
                            target_name
                        ),
                    );
                }
            }
        }

        Ok(RunStatus::Executed)
    }
}

/// Ensure libra-workspace-hack is a dependency
#[derive(Debug)]
pub struct WorkspaceHack;

impl Linter for WorkspaceHack {
    fn name(&self) -> &'static str {
        "workspace-hack"
    }
}

impl PackageLinter for WorkspaceHack {
    fn run<'l>(
        &self,
        ctx: &PackageContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let package_id = ctx.metadata().id();
        let pkg_graph = ctx
            .project_ctx()
            .package_graph()
            .expect("can't find package graph");
        let workspace_hack_id = pkg_graph
            .workspace()
            .member_by_name("libra-workspace-hack")
            .expect("can't find libra-workspace-hack package")
            .id();

        // libra-workspace-hack does not need to depend on itself
        if package_id == workspace_hack_id {
            return Ok(RunStatus::Executed);
        }

        let has_links = pkg_graph
            .dep_links(package_id)
            .expect("valid package ID")
            .next()
            .is_some();
        let has_hack_dep = pkg_graph
            .directly_depends_on(package_id, workspace_hack_id)
            .expect("valid package ID");
        if has_links && !has_hack_dep {
            out.write(LintLevel::Error, "missing libra-workspace-hack dependency");
        }

        Ok(RunStatus::Executed)
    }
}
<|endoftext|>


<commit_before>use {alloc, Buf, MutBuf};
use std::{cmp, fmt, io, ptr};

/// Buf backed by a continous chunk of memory. Maintains a read cursor and a
/// write cursor. When reads and writes reach the end of the allocated buffer,
/// wraps around to the start.
pub struct RingBuf {
    ptr: alloc::MemRef,  // Pointer to the memory
    cap: usize,          // Capacity of the buffer
    pos: usize,          // Offset of read cursor
    len: usize           // Number of bytes to read
}

// TODO: There are most likely many optimizations that can be made
impl RingBuf {
    pub fn new(mut capacity: usize) -> RingBuf {
        // Handle the 0 length buffer case
        if capacity == 0 {
            return RingBuf {
                ptr: alloc::MemRef::none(),
                cap: 0,
                pos: 0,
                len: 0
            }
        }

        // Round to the next power of 2 for better alignment
        capacity = capacity.next_power_of_two();

        let mem = alloc::heap(capacity as usize);

        RingBuf {
            ptr: mem,
            cap: capacity,
            pos: 0,
            len: 0
        }
    }

    pub fn is_full(&self) -> bool {
        self.cap == self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn capacity(&self) -> usize {
        self.cap
    }

    fn read_remaining(&self) -> usize {
        self.len
    }

    fn write_remaining(&self) -> usize {
        self.cap - self.len
    }

    fn advance_reader(&mut self, mut cnt: usize) {
        if self.cap == 0 {
            return;
        }
        cnt = cmp::min(cnt, self.read_remaining());

        self.pos += cnt;
        self.pos %= self.cap;
        self.len -= cnt;
    }

    fn advance_writer(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.write_remaining());
        self.len += cnt;
    }
}

impl Clone for RingBuf {
    fn clone(&self) -> RingBuf {
        use std::cmp;

        let mut ret = RingBuf::new(self.cap);

        ret.pos = self.pos;
        ret.len = self.len;

        unsafe {
            let to = self.pos + self.len;

            if to > self.cap {
                ptr::copy(self.ptr.ptr() as *const u8, ret.ptr.ptr(), to % self.cap);
            }

            ptr::copy(
                self.ptr.ptr().offset(self.pos as isize) as *const u8,
                ret.ptr.ptr().offset(self.pos as isize),
                cmp::min(self.len, self.cap - self.pos));
        }

        ret
    }

    // TODO: an improved version of clone_from is possible that potentially
    // re-uses the buffer
}

impl fmt::Debug for RingBuf {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "RingBuf[.. {}]", self.len)
    }
}

impl Buf for RingBuf {

    fn remaining(&self) -> usize {
        self.read_remaining()
    }

    fn bytes(&self) -> &[u8] {
        let mut to = self.pos + self.len;

        if to > self.cap {
            to = self.cap
        }

        &self.ptr.bytes()[self.pos .. to]
    }

    fn advance(&mut self, cnt: usize) {
        self.advance_reader(cnt)
    }
}

impl MutBuf for RingBuf {

    fn remaining(&self) -> usize {
        self.write_remaining()
    }

    fn advance(&mut self, cnt: usize) {
        self.advance_writer(cnt)
    }

    fn mut_bytes(&mut self) -> &mut [u8] {
        if self.cap == 0 {
            return self.ptr.bytes_mut();
        }
        let mut from;
        let mut to;

        from = self.pos + self.len;
        from %= self.cap;

        to = from + <Self as MutBuf>::remaining(&self);

        if to >= self.cap {
            to = self.cap;
        }

        &mut self.ptr.bytes_mut()[from..to]
    }
}

impl io::Read for RingBuf {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if !MutBuf::has_remaining(self) {
            return Ok(0);
        }

        Ok(self.read_slice(buf))
    }
}

impl io::Write for RingBuf {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if !Buf::has_remaining(self) {
            return Ok(0);
        }

        Ok(self.write_slice(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

unsafe impl Send for RingBuf { }
<commit_msg>Fix bugs in RingBuf's io::Read and io::Write instance<commit_after>use {alloc, Buf, MutBuf};
use std::{cmp, fmt, io, ptr};

/// Buf backed by a continous chunk of memory. Maintains a read cursor and a
/// write cursor. When reads and writes reach the end of the allocated buffer,
/// wraps around to the start.
pub struct RingBuf {
    ptr: alloc::MemRef,  // Pointer to the memory
    cap: usize,          // Capacity of the buffer
    pos: usize,          // Offset of read cursor
    len: usize           // Number of bytes to read
}

// TODO: There are most likely many optimizations that can be made
impl RingBuf {
    pub fn new(mut capacity: usize) -> RingBuf {
        // Handle the 0 length buffer case
        if capacity == 0 {
            return RingBuf {
                ptr: alloc::MemRef::none(),
                cap: 0,
                pos: 0,
                len: 0
            }
        }

        // Round to the next power of 2 for better alignment
        capacity = capacity.next_power_of_two();

        let mem = alloc::heap(capacity as usize);

        RingBuf {
            ptr: mem,
            cap: capacity,
            pos: 0,
            len: 0
        }
    }

    pub fn is_full(&self) -> bool {
        self.cap == self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn capacity(&self) -> usize {
        self.cap
    }

    fn read_remaining(&self) -> usize {
        self.len
    }

    fn write_remaining(&self) -> usize {
        self.cap - self.len
    }

    fn advance_reader(&mut self, mut cnt: usize) {
        if self.cap == 0 {
            return;
        }
        cnt = cmp::min(cnt, self.read_remaining());

        self.pos += cnt;
        self.pos %= self.cap;
        self.len -= cnt;
    }

    fn advance_writer(&mut self, mut cnt: usize) {
        cnt = cmp::min(cnt, self.write_remaining());
        self.len += cnt;
    }
}

impl Clone for RingBuf {
    fn clone(&self) -> RingBuf {
        use std::cmp;

        let mut ret = RingBuf::new(self.cap);

        ret.pos = self.pos;
        ret.len = self.len;

        unsafe {
            let to = self.pos + self.len;

            if to > self.cap {
                ptr::copy(self.ptr.ptr() as *const u8, ret.ptr.ptr(), to % self.cap);
            }

            ptr::copy(
                self.ptr.ptr().offset(self.pos as isize) as *const u8,
                ret.ptr.ptr().offset(self.pos as isize),
                cmp::min(self.len, self.cap - self.pos));
        }

        ret
    }

    // TODO: an improved version of clone_from is possible that potentially
    // re-uses the buffer
}

impl fmt::Debug for RingBuf {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "RingBuf[.. {}]", self.len)
    }
}

impl Buf for RingBuf {

    fn remaining(&self) -> usize {
        self.read_remaining()
    }

    fn bytes(&self) -> &[u8] {
        let mut to = self.pos + self.len;

        if to > self.cap {
            to = self.cap
        }

        &self.ptr.bytes()[self.pos .. to]
    }

    fn advance(&mut self, cnt: usize) {
        self.advance_reader(cnt)
    }
}

impl MutBuf for RingBuf {

    fn remaining(&self) -> usize {
        self.write_remaining()
    }

    fn advance(&mut self, cnt: usize) {
        self.advance_writer(cnt)
    }

    fn mut_bytes(&mut self) -> &mut [u8] {
        if self.cap == 0 {
            return self.ptr.bytes_mut();
        }
        let mut from;
        let mut to;

        from = self.pos + self.len;
        from %= self.cap;

        to = from + <Self as MutBuf>::remaining(&self);

        if to >= self.cap {
            to = self.cap;
        }

        &mut self.ptr.bytes_mut()[from..to]
    }
}

impl io::Read for RingBuf {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if !Buf::has_remaining(self) {
            return Ok(0);
        }

        Ok(self.read_slice(buf))
    }
}

impl io::Write for RingBuf {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if !MutBuf::has_remaining(self) {
            return Ok(0);
        }

        Ok(self.write_slice(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

unsafe impl Send for RingBuf { }
<|endoftext|>


<commit_before>use type_sys::Value;

// The warning "unused_assignments" is allowed in many of these functions because there in the last
// argument of the "get_args!" macro, the assignment "count += 1" is unused.

macro_rules! get_args {
    ( $args:expr, $( $type:path ),* ) => {
        {
            let mut count = 0;
            (
                $(
                    if let &$type(val) = $args.get(count).expect("Wrong number of arguments") {
                        count += 1;
                        val
                    } else {
                        panic!("Wrong arguments");
                    },
                )*
            )
        }
    };
}

pub fn resolve_func(name: String, args: Vec<Value>) -> Value {
    match name.as_ref() {
        "print" => print(args),
        _ => panic!("Unknown function: {}/{}", name, args.len()),
    }
}

pub fn print(args: Vec<Value>) -> Value {
    use self::Value::*;
    //let (val,) = get_args!(args, Integer);
    let val = args.get(0).expect("Wrong number of arguments");
    println!("=> {}", val);
    Void
}

//===========================
//== Arithmetic operations ==
//===========================

#[allow(unused_assignments)]
pub fn un_plus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (val,) = get_args!(args, Integer);
    Integer(val)
}

#[allow(unused_assignments)]
pub fn un_minus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (val,) = get_args!(args, Integer);
    Integer(-val)
}

#[allow(unused_assignments)]
pub fn plus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Integer(lhs + rhs)
}

#[allow(unused_assignments)]
pub fn minus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Integer(lhs - rhs)
}

#[allow(unused_assignments)]
pub fn mul(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Integer(lhs * rhs)
}

#[allow(unused_assignments)]
pub fn div(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Integer(lhs / rhs)
}

//========================
//== Logical Operations ==
//========================

#[allow(unused_assignments)]
pub fn lower(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Bool(lhs < rhs)
}

#[allow(unused_assignments)]
pub fn lower_eq(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Bool(lhs <= rhs)
}

#[allow(unused_assignments)]
pub fn greater(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Bool(lhs > rhs)
}

#[allow(unused_assignments)]
pub fn greater_eq(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (lhs, rhs) = get_args!(args, Integer, Integer);
    Bool(lhs >= rhs)
}

#[allow(unused_assignments)]
pub fn equal(args: Vec<Value>) -> Value {
    use self::Value::*;
    let lhs = args.get(0).expect("Wrong number of arguments");
    let rhs = args.get(1).expect("Wrong number of arguments");
    Bool(lhs == rhs)
}

#[allow(unused_assignments)]
pub fn not_equal(args: Vec<Value>) -> Value {
    use self::Value::*;
    let lhs = args.get(0).expect("Wrong number of arguments");
    let rhs = args.get(1).expect("Wrong number of arguments");
    Bool(lhs != rhs)
}
<commit_msg>Use macro to define most operator builtins<commit_after>use type_sys::Value;

// The warning "unused_assignments" is allowed in many of these functions because there in the last
// argument of the "get_args!" macro, the assignment "count += 1" is unused.

macro_rules! get_args {
    ( $args:expr, $( $type:path ),* ) => {
        {
            let mut count = 0;
            (
                $(
                    if let &$type(val) = $args.get(count).expect("Wrong number of arguments") {
                        count += 1;
                        val
                    } else {
                        panic!("Wrong arguments");
                    },
                )*
            )
        }
    };
}

macro_rules! define_operator {
    ( $symbol:tt, $func_name:ident, $ret_type:path ) => {

        #[allow(unused_assignments)]
        pub fn $func_name(args: Vec<Value>) -> Value {
            use self::Value::*;
            let (lhs, rhs) = get_args!(args, Integer, Integer);
            $ret_type(lhs $symbol rhs)
        }

    }
}

pub fn resolve_func(name: String, args: Vec<Value>) -> Value {
    match name.as_ref() {
        "print" => print(args),
        _ => panic!("Unknown function: {}/{}", name, args.len()),
    }
}

pub fn print(args: Vec<Value>) -> Value {
    use self::Value::*;
    //let (val,) = get_args!(args, Integer);
    let val = args.get(0).expect("Wrong number of arguments");
    println!("=> {}", val);
    Void
}

//===========================
//== Arithmetic operations ==
//===========================

#[allow(unused_assignments)]
pub fn un_plus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (val,) = get_args!(args, Integer);
    Integer(val)
}

#[allow(unused_assignments)]
pub fn un_minus(args: Vec<Value>) -> Value {
    use self::Value::*;
    let (val,) = get_args!(args, Integer);
    Integer(-val)
}

define_operator!(+, plus,  Integer);
define_operator!(-, minus, Integer);
define_operator!(*, mul,   Integer);
define_operator!(/, div,   Integer);

//========================
//== Logical Operations ==
//========================

define_operator!(<,  lower,      Bool);
define_operator!(<=, lower_eq,   Bool);
define_operator!(>,  greater,    Bool);
define_operator!(>=, greater_eq, Bool);

#[allow(unused_assignments)]
pub fn equal(args: Vec<Value>) -> Value {
    use self::Value::*;
    let lhs = args.get(0).expect("Wrong number of arguments");
    let rhs = args.get(1).expect("Wrong number of arguments");
    Bool(lhs == rhs)
}

#[allow(unused_assignments)]
pub fn not_equal(args: Vec<Value>) -> Value {
    use self::Value::*;
    let lhs = args.get(0).expect("Wrong number of arguments");
    let rhs = args.get(1).expect("Wrong number of arguments");
    Bool(lhs != rhs)
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use collections::*;

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Op { Plus, Minus, Times, Divide }

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Tok {
  Num(isize),
  Op(Op)
}

fn pop<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>) -> (X, List<X>) {
    List::elim_arg(stack, (),
                   |_,_|    panic!("cannot pop an empty stack"),
                   |h,t, _| (h, t),
                   |_,t, _| pop(t))
}

fn push<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>, elm:X) -> List<X>
{
  List::cons(elm, stack)
}

fn precedence_check (top_op:&Op, next_op:&Op) -> bool {
  match (*top_op, *next_op) {
    (Op::Plus,  _       ) => false,
    (Op::Minus, Op::Plus) => true, // Critical!
    (Op::Minus, _       ) => false,

    (Op::Times, Op::Plus)   => true,
    (Op::Times, Op::Minus)  => true,
    (Op::Times, Op::Divide) => true,

    (Op::Divide, Op::Plus)  => true,
    (Op::Divide, Op::Minus) => true,
    (Op::Divide, Op::Times) => true,
    _ => false
  }
}

pub fn postfix_of_infix(infix: Tree<Tok>) -> List<Tok> {
  let (ops, postfix) : (List<Op>, List<Tok>) =
    tree_fold_seq
    (infix, Dir2::Left, (List::Nil, List::Nil),
     Rc::new(|tok, (ops, postfix)| {
       match tok {
         Tok::Num(n) => (ops, push(postfix, Tok::Num(n))),
         Tok::Op(op) => {
           if List::is_empty(&ops) { (push(ops, op), postfix) }
           else {
             fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
               if List::is_empty(&ops) { (ops, postfix) } else {
                 let (top_op, popped_ops) = pop(ops);
                 if precedence_check (&top_op, &op) {
                   myloop (op, popped_ops, push(postfix, Tok::Op(top_op)))
                 }
                 else {
                   (push(popped_ops, top_op), postfix)
                 }
               }};
             let (ops, postfix) = myloop(op, ops, postfix) ;
             (push(ops, op), postfix)
           }
         }
       }}),
     Rc::new(|_, a|   a),
     Rc::new(|_,_, a| a),
     );
  fn myloop (ops:List<Op>, postfix:List<Tok>) -> (List<Tok>) {
    if List::is_empty(&ops) { postfix } else {
      let (top_op, popped_ops) = pop(ops);
      myloop (popped_ops, push(postfix, Tok::Op(top_op)))
    }};
  myloop(ops, postfix)
}

#[test]
fn test_postfix_of_infix () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(3) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = postfix_of_infix(tree);
  println!("{:?}", ans)
}

pub fn evaluate_postfix(input: Tree<Tok>) -> isize {
  let stack =
    tree_fold_seq
    (input, Dir2::Left, List::Nil,
     Rc::new(|tok, stack| {
       match tok {
         Tok::Op(op) => {
           let (x,stack) = pop(stack);
           let (y,stack) = pop(stack);
           let z = match op {
             Op::Plus   => y + x,
             Op::Minus  => y - x,
             Op::Times  => y * x,
             Op::Divide => y / x,
           };
           push(stack, z)
         },
         Tok::Num(n) => {
           push(stack, n)
         }
       }}),
     Rc::new(|_, stack|   stack),
     Rc::new(|_,_, stack| stack),
     );
  let (x, stack) = pop(stack);
  assert!(List::is_empty(&stack));
  x
}

#[test]
fn test_eval_postfix () {
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(22) ),
    NameElse::Else( Tok::Num(33) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(12) ),
    NameElse::Else( Tok::Num(13) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = evaluate_postfix(tree);
  assert_eq!(ans, 86)
}

#[test]
fn test_arith_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(1) ),
    // NameElse::Else( Tok::Op(Op::Plus) ),
    // NameElse::Else( Tok::Num(1) ),
    // NameElse::Else( Tok::Op(Op::Times) ),
    // NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Divide) ),
    // NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Minus) ),
    // NameElse::Else( Tok::Num(3) ),
    ];
  println!("{:?}", &input);
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let list  : List<Tok> = postfix_of_infix(tree);
  println!("{:?}", &list);
  //let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
  let ans = evaluate_postfix(tree);
  println!("{:?}", ans)
}

// fn infix_to_postfix(input: List<char>) -> List<List<char>> {
//   let mut postfix = List::Nil;
//   let mut operators = List::Nil;
//   let mut temp_operand = List::Nil;

//   //'''
//   // Match all chars in 'input'
//   //     If c is a digit, then push onto a Digit list temporarily until all digits in an operand are read (after which add the operand to the Postfix list)
//   //     If c is an operator, pop everything of higher predence and add it to postfix list, then add c to list of Operators
//   //'''
//   postfix
// }

// fn evaluate_postfix(input: List<List<char>>) -> i32 {

//   //'''
//   // Convert list to number and apply operation on two at a time
//   //'''

// }

// fn test_arith_parse() {
//   println!("Enter expression in infix format");
//   let mut input = String::new();
//   io::stdin().read_line(&mut input).ok().expect("Failed to parse input");

//   // '''
//   // Convert string to List<char> (reverse order)
//   // '''
//   let input_list = panic!("");

//   let postfix_expr = infix_to_postfix(input_list);
//   let result = evaluate_postfix(postfix_expr);
// }

<commit_msg>Write Precedence enum, handle the case when operators are of equal precedence, add test<commit_after>use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use collections::*;

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Op { Plus, Minus, Times, Divide }

#[derive(Clone,Copy,Hash,Eq,PartialEq,Debug)]
pub enum Tok {
  Num(isize),
  Op(Op)
}

pub enum Precedence { Higher, Lower, Equal }

fn pop<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>) -> (X, List<X>) {
    List::elim_arg(stack, (),
                   |_,_|    panic!("cannot pop an empty stack"),
                   |h,t, _| (h, t),
                   |_,t, _| pop(t))
}

fn push<X:Clone+Copy+Hash+Eq+PartialEq+Debug>
  (stack:List<X>, elm:X) -> List<X>
{
  List::cons(elm, stack)
}

fn precedence_check (top_op:&Op, next_op:&Op) -> Precedence {
  match (*top_op, *next_op) {
    (Op::Plus, Op::Plus) => Precedence::Equal,
    (Op::Plus, Op::Minus) => Precedence::Equal,
    (Op::Minus, Op::Plus) => Precedence::Equal,
    (Op::Minus, Op::Minus) => Precedence::Equal,

    (Op::Times, Op::Times) => Precedence::Equal,
    (Op::Divide, Op::Divide) => Precedence::Equal,

    (Op::Times, Op::Plus)   => Precedence::Higher,
    (Op::Times, Op::Minus)  => Precedence::Higher,
    (Op::Times, Op::Divide) => Precedence::Higher,

    (Op::Divide, Op::Plus)  => Precedence::Higher,
    (Op::Divide, Op::Minus) => Precedence::Higher,
    (Op::Divide, Op::Times) => Precedence::Higher,
    _ => Precedence::Lower
  }
}

pub fn postfix_of_infix(infix: Tree<Tok>) -> List<Tok> {
  let (ops, postfix) : (List<Op>, List<Tok>) =
    tree_fold_seq
    (infix, Dir2::Left, (List::Nil, List::Nil),
     Rc::new(|tok, (ops, postfix)| {
       match tok {
         Tok::Num(n) => (ops, push(postfix, Tok::Num(n))),
         Tok::Op(op) => {
           if List::is_empty(&ops) { (push(ops, op), postfix) }
           else {
             fn myloop (op:Op, ops:List<Op>, postfix:List<Tok>) -> (List<Op>, List<Tok>) {
               if List::is_empty(&ops) { (ops, postfix) } else {
                 let (top_op, popped_ops) = pop(ops);
                 match precedence_check (&top_op, &op) {
                   Precedence::Higher => { myloop (op, popped_ops, push(postfix, Tok::Op(top_op)))},
		   Precedence::Equal => {(popped_ops, push(postfix, Tok::Op(top_op)))},
                   _ => {(push(popped_ops, top_op), postfix)}
                 }
               }};
             let (ops, postfix) = myloop(op, ops, postfix) ;
             (push(ops, op), postfix)
           }
         }
       }}),
     Rc::new(|_, a|   a),
     Rc::new(|_,_, a| a),
     );
  fn myloop (ops:List<Op>, postfix:List<Tok>) -> (List<Tok>) {
    if List::is_empty(&ops) { postfix } else {
      let (top_op, popped_ops) = pop(ops);
      myloop (popped_ops, push(postfix, Tok::Op(top_op)))
    }};
  myloop(ops, postfix)
}

#[test]
fn test_postfix_of_infix () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(3) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = postfix_of_infix(tree);
  println!("{:?}", ans)
}

pub fn evaluate_postfix(input: Tree<Tok>) -> isize {
  let stack =
    tree_fold_seq
    (input, Dir2::Left, List::Nil,
     Rc::new(|tok, stack| {
       match tok {
         Tok::Op(op) => {
           let (x,stack) = pop(stack);
           let (y,stack) = pop(stack);
           let z = match op {
             Op::Plus   => y + x,
             Op::Minus  => y - x,
             Op::Times  => y * x,
             Op::Divide => y / x,
           };
           push(stack, z)
         },
         Tok::Num(n) => {
           push(stack, n)
         }
       }}),
     Rc::new(|_, stack|   stack),
     Rc::new(|_,_, stack| stack),
     );
  let (x, stack) = pop(stack);
  assert!(List::is_empty(&stack));
  x
}

#[test]
fn test_eval_postfix () {
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(22) ),
    NameElse::Else( Tok::Num(33) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(12) ),
    NameElse::Else( Tok::Num(13) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    ];
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let ans = evaluate_postfix(tree);
  assert_eq!(ans, 86)
}

#[test]
fn test_arith_eval () {

  // 1 * 2 + 3
  let input = vec![
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Plus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(3) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(1) ),
    NameElse::Else( Tok::Op(Op::Minus) ),
    NameElse::Else( Tok::Num(2) ),
    NameElse::Else( Tok::Op(Op::Times) ),
    NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Divide) ),
    // NameElse::Else( Tok::Num(2) ),
    // NameElse::Else( Tok::Op(Op::Minus) ),
    // NameElse::Else( Tok::Num(3) ),
    ];
  println!("{:?}", &input);
  let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Left, input);
  let list  : List<Tok> = postfix_of_infix(tree);
  println!("{:?}", &list);
  //let input : List<Tok> = list_of_vec(&input);
  let tree  : Tree<Tok> = tree_of_list(Dir2::Right, list);
  let ans = evaluate_postfix(tree);
  println!("{:?}", ans)
}

// fn infix_to_postfix(input: List<char>) -> List<List<char>> {
//   let mut postfix = List::Nil;
//   let mut operators = List::Nil;
//   let mut temp_operand = List::Nil;

//   //'''
//   // Match all chars in 'input'
//   //     If c is a digit, then push onto a Digit list temporarily until all digits in an operand are read (after which add the operand to the Postfix list)
//   //     If c is an operator, pop everything of higher predence and add it to postfix list, then add c to list of Operators
//   //'''
//   postfix
// }

// fn evaluate_postfix(input: List<List<char>>) -> i32 {

//   //'''
//   // Convert list to number and apply operation on two at a time
//   //'''

// }

// fn test_arith_parse() {
//   println!("Enter expression in infix format");
//   let mut input = String::new();
//   io::stdin().read_line(&mut input).ok().expect("Failed to parse input");

//   // '''
//   // Convert string to List<char> (reverse order)
//   // '''
//   let input_list = panic!("");

//   let postfix_expr = infix_to_postfix(input_list);
//   let result = evaluate_postfix(postfix_expr);
// }

<|endoftext|>