<commit_before>// Purge all references to a crate's version from the database.
//
// Please be super sure you want to do this before running this.
//
// Usage:
//      cargo run --bin delete-version crate-name version-number

#![deny(warnings)]

extern crate cargo_registry;
extern crate git2;
extern crate postgres;
extern crate rustc_serialize;

use std::path::PathBuf;

use cargo_registry::{http, env, App};
use cargo_registry::util::{CargoResult, human};

#[allow(dead_code)]
fn main() {
    git2::Repository::init("tmp/test").unwrap();
    let config = cargo_registry::Config {
        s3_bucket: String::new(),
        s3_access_key: String::new(),
        s3_secret_key: String::new(),
        s3_region: None,
        s3_proxy: None,
        session_key: String::new(),
        git_repo_checkout: PathBuf::from("tmp/test"),
        gh_client_id: env("GH_CLIENT_ID"),
        gh_client_secret: env("GH_CLIENT_SECRET"),
        db_url: env("DATABASE_URL"),
        env: cargo_registry::Env::Production,
        max_upload_size: 0,
    };
    let app = cargo_registry::App::new(&config);
    {
        let tx = app.database.get().unwrap();
        let tx = tx.transaction().unwrap();
        update(&app, &tx);
        tx.set_commit();
        tx.finish().unwrap();
    }
}

#[derive(RustcDecodable)]
struct GithubUser {
    login: String,
    id: i32,
}

fn update(app: &App, tx: &postgres::Transaction) {
    let mut rows = Vec::new();
    let query = "SELECT id, gh_login, gh_access_token, gh_avatar FROM users \
                  WHERE gh_id IS NULL";
    for row in &tx.query(query, &[]).unwrap() {
        let id: i32 = row.get("id");
        let login: String = row.get("gh_login");
        let token: String = row.get("gh_access_token");
        let avatar: Option<String> = row.get("gh_avatar");
        rows.push((id, login, http::token(token), avatar));
    }

    for (id, login, token, avatar) in rows {
        println!("attempt: {}/{}", id, login);
        let res = (|| -> CargoResult<()> {
            let resp = try!(http::github(app, "/user", &token));
            let ghuser: GithubUser = try!(http::parse_github_response(resp));
            if let Some(ref avatar) = avatar {
                if !avatar.contains(&ghuser.id.to_string()) {
                    return Err(human(format!("avatar: {}", avatar)))
                }
            }
            if ghuser.login == login {
                try!(tx.execute("UPDATE users SET gh_id = $1 WHERE id = $2",
                                &[&ghuser.id, &id]));
                Ok(())
            } else {
                Err(human(format!("different login: {}", ghuser.login)))
            }
        })();
        if let Err(e) = res {
            println!("error for {}: {:?}", login, e);
        }
    }
}

<commit_msg>Fill in ids from `/users/$foo`<commit_after>// Purge all references to a crate's version from the database.
//
// Please be super sure you want to do this before running this.
//
// Usage:
//      cargo run --bin delete-version crate-name version-number

#![deny(warnings)]

extern crate cargo_registry;
extern crate git2;
extern crate postgres;
extern crate rustc_serialize;

use std::path::PathBuf;

use cargo_registry::{http, env, App};
use cargo_registry::util::{CargoResult, human};

#[allow(dead_code)]
fn main() {
    git2::Repository::init("tmp/test").unwrap();
    let config = cargo_registry::Config {
        s3_bucket: String::new(),
        s3_access_key: String::new(),
        s3_secret_key: String::new(),
        s3_region: None,
        s3_proxy: None,
        session_key: String::new(),
        git_repo_checkout: PathBuf::from("tmp/test"),
        gh_client_id: env("GH_CLIENT_ID"),
        gh_client_secret: env("GH_CLIENT_SECRET"),
        db_url: env("DATABASE_URL"),
        env: cargo_registry::Env::Production,
        max_upload_size: 0,
    };
    let app = cargo_registry::App::new(&config);
    {
        let tx = app.database.get().unwrap();
        let tx = tx.transaction().unwrap();
        update(&app, &tx);
        tx.set_commit();
        tx.finish().unwrap();
    }
}

#[derive(RustcDecodable)]
struct GithubUser {
    login: String,
    id: i32,
}

fn update(app: &App, tx: &postgres::Transaction) {
    let mut rows = Vec::new();
    let query = "SELECT id, gh_login, gh_access_token, gh_avatar FROM users \
                  WHERE gh_id IS NULL";
    for row in &tx.query(query, &[]).unwrap() {
        let id: i32 = row.get("id");
        let login: String = row.get("gh_login");
        let token: String = row.get("gh_access_token");
        let avatar: Option<String> = row.get("gh_avatar");
        rows.push((id, login, http::token(token), avatar));
    }

    for (id, login, token, avatar) in rows {
        println!("attempt: {}/{}", id, login);
        let res = (|| -> CargoResult<()> {
            let url = format!("/users/{}", login);
            let resp = try!(http::github(app, &url, &token));
            let ghuser: GithubUser = try!(http::parse_github_response(resp));
            if let Some(ref avatar) = avatar {
                if !avatar.contains(&ghuser.id.to_string()) {
                    return Err(human(format!("avatar: {}", avatar)))
                }
            }
            if ghuser.login == login {
                try!(tx.execute("UPDATE users SET gh_id = $1 WHERE id = $2",
                                &[&ghuser.id, &id]));
                Ok(())
            } else {
                Err(human(format!("different login: {}", ghuser.login)))
            }
        })();
        if let Err(e) = res {
            println!("error for {}: {:?}", login, e);
        }
    }
}

<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Rosetta_Code/Find_unimplemented_tasks
extern crate hyper;
extern crate rustc_serialize;

use std::collections::HashSet;
use std::io::prelude::*;

use hyper::Client;
use hyper::header::Connection;
use rustc_serialize::json::Json;

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Task {
    pub id: String,
    pub title: String,
}

struct Category {
    name: String,
    cmcontinue: Option<String>,
    http_client: Client,
    is_first_iteration: bool,
}

impl Category {
    fn new(name: &str) -> Category {
        Category {
            name: name.to_owned(),
            cmcontinue: None,
            http_client: Client::new(),
            is_first_iteration: true,
        }
    }
}

impl Iterator for Category {
    type Item = Vec<Task>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.cmcontinue.is_none() && !self.is_first_iteration {
            return None;
        }

        self.is_first_iteration = false;

        let cmcontinue = match self.cmcontinue {
            Some(ref cmcontinue) => format!("&cmcontinue={}", cmcontinue),
            None => "".to_owned(),
        };

        let url = &format!("http://rosettacode.org/mw/api.\
                            php?action=query&list=categorymembers&cmtitle=Category:\
                            {}&cmlimit=500&format=json{}",
                           self.name,
                           cmcontinue);

        let mut res = self.http_client
                          .get(url)
                          .header(Connection::close())
                          .send()
                          .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        let json = Json::from_str(&body).unwrap();
        let tasks = json.as_object()
                        .unwrap()
                        .get("query")
                        .unwrap()
                        .as_object()
                        .unwrap()
                        .get("categorymembers")
                        .unwrap()
                        .as_array()
                        .unwrap()
                        .iter()
                        .map(|cm| {
                            let cm_obj = cm.as_object().unwrap();
                            Task {
                                id: cm_obj.get("pageid")
                                          .unwrap()
                                          .as_u64()
                                          .unwrap()
                                          .to_string(),
                                title: cm_obj.get("title")
                                             .unwrap()
                                             .as_string()
                                             .unwrap()
                                             .to_owned(),
                            }
                        })
                        .collect::<Vec<_>>();

        self.cmcontinue = json.as_object()
                              .unwrap()
                              .get("query-continue")
                              .map(|query_continue| {
                                  query_continue.as_object()
                                                .unwrap()
                                                .get("categorymembers")
                                                .unwrap()
                                                .as_object()
                                                .unwrap()
                                                .get("cmcontinue")
                                                .unwrap()
                                                .as_string()
                                                .unwrap()
                                                .to_owned()
                              });

        Some(tasks)
    }
}

pub fn all_tasks() -> Vec<Task> {
    Category::new("Programming Tasks")
        .flat_map(|tasks| tasks)
        .collect()
}

pub fn unimplemented_tasks(lang: &str) -> Vec<Task> {
    let all_tasks = all_tasks().iter().cloned().collect::<HashSet<_>>();
    let implemented_tasks = Category::new(lang)
                                .flat_map(|tasks| tasks)
                                .collect::<HashSet<_>>();
    let mut unimplemented_tasks = all_tasks.difference(&implemented_tasks)
                                           .cloned()
                                           .collect::<Vec<Task>>();
    unimplemented_tasks.sort_by(|a, b| a.title.cmp(&b.title));
    unimplemented_tasks
}

pub fn main() {
    for task in unimplemented_tasks("Rust") {
        println!("{:6} {}", task.id, task.title);
    }
}
<commit_msg>improve error handling in find_unimplemented_tasks<commit_after>// http://rosettacode.org/wiki/Rosetta_Code/Find_unimplemented_tasks
extern crate hyper;
extern crate rustc_serialize;
extern crate url;

use std::collections::{BTreeMap, HashSet};
use std::io::prelude::*;

use hyper::Client;
use hyper::header::Connection;
use rustc_serialize::json::{self, Json};
use self::url::Url;

/// A Rosetta Code task.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Task {
    /// The ID of the page containing the task in the MediaWiki API.
    pub id: u64,

    /// The human-readable title of the task.
    pub title: String,
}

/// Encapsulates errors that might occur during JSON parsing.
#[derive(Debug)]
enum TaskParseError {
    /// Something went wrong with the HTTP request to the API.
    Http(hyper::Error),

    /// There was a problem parsing the API response into JSON.
    Json(json::ParserError),

    /// The response JSON contained unexpected keys or values.
    UnexpectedFormat,
}

impl From<json::ParserError> for TaskParseError {
    fn from(err: json::ParserError) -> Self {
        TaskParseError::Json(err)
    }
}

impl From<hyper::Error> for TaskParseError {
    fn from(err: hyper::Error) -> Self {
        TaskParseError::Http(err)
    }
}

/// Represents a category of pages on Rosetta Code, such as "Rust".
struct Category {
    name: String,
    continue_params: Option<BTreeMap<String, String>>,
}

impl Category {
    fn new(name: &str) -> Category {
        let mut continue_params = BTreeMap::new();
        continue_params.insert("continue".to_owned(), "".to_owned());

        Category {
            name: name.to_owned(),
            continue_params: Some(continue_params),
        }
    }
}

/// Sends a request to Rosetta Code through the MediaWiki API. If successful, returns the response
/// as a JSON object.
fn query_api(category_name: &str,
             continue_params: &BTreeMap<String, String>)
             -> Result<Json, TaskParseError> {
    let mut url = Url::parse("http://rosettacode.org/mw/api.php").unwrap();
    let category_param = format!("Category:{}", category_name);
    let mut api_parameters = vec![("action", "query"),
                                  ("list", "categorymembers"),
                                  ("cmtitle", &category_param),
                                  ("cmlimit", "500"),
                                  ("format", "json")];

    for (key, value) in continue_params {
        api_parameters.push((key, value));
    }

    url.set_query_from_pairs(api_parameters.into_iter());
    let mut response = try!(Client::new()
                                .get(&url.serialize())
                                .header(Connection::close())
                                .send());
    let mut body = String::new();
    response.read_to_string(&mut body).unwrap();

    Json::from_str(&body).map_err(|err| From::from(err))
}

/// Given a JSON object, parses the task information from the MediaWiki API response.
fn parse_tasks(json: &Json) -> Result<Vec<Task>, TaskParseError> {
    let tasks_json = try!(json.find_path(&["query", "categorymembers"])
                              .and_then(|tasks| tasks.as_array())
                              .ok_or(TaskParseError::UnexpectedFormat));

    tasks_json.iter()
              .map(|task_json| {
                  let id = try!(task_json.find("pageid")
                                         .and_then(|id| id.as_u64())
                                         .ok_or(TaskParseError::UnexpectedFormat));
                  let title = try!(task_json.find("title")
                                            .and_then(|title| title.as_string())
                                            .ok_or(TaskParseError::UnexpectedFormat));
                  let task = Task {
                      id: id,
                      title: title.to_owned(),
                  };

                  Ok(task)
              })
              .collect()
}

impl Iterator for Category {
    type Item = Vec<Task>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.continue_params.is_none() {
            return None;
        }

        query_api(&self.name, &self.continue_params.clone().unwrap())
            .and_then(|result| {
                // If there are more pages of results to request, save them for the next iteration.
                self.continue_params = result.find("continue")
                                             .and_then(|continue_params| {
                                                 continue_params.as_object()
                                             })
                                             .map(|continue_params| {
                                                 continue_params.iter()
                                                                .map(|(key, value)| {
                                                                    (key.to_owned(),
                                                                     value.as_string()
                                                                          .unwrap()
                                                                          .to_owned())
                                                                })
                                                                .collect()
                                             });

                parse_tasks(&result)
            })
            .map_err(|err| println!("Error parsing response: {:?}", err))
            .ok()
    }
}

pub fn all_tasks() -> Vec<Task> {
    Category::new("Programming Tasks")
        .flat_map(|tasks| tasks)
        .collect()
}

pub fn unimplemented_tasks(lang: &str) -> Vec<Task> {
    let all_tasks = all_tasks().iter().cloned().collect::<HashSet<_>>();
    let implemented_tasks = Category::new(lang)
                                .flat_map(|tasks| tasks)
                                .collect::<HashSet<_>>();
    let mut unimplemented_tasks = all_tasks.difference(&implemented_tasks)
                                           .cloned()
                                           .collect::<Vec<Task>>();
    unimplemented_tasks.sort_by(|a, b| a.title.cmp(&b.title));
    unimplemented_tasks
}

pub fn main() {
    for task in unimplemented_tasks("Rust") {
        println!("{:6} {}", task.id, task.title);
    }
}
<|endoftext|>


<commit_before>use std::{collections::BTreeSet, fmt, io};

use html5ever::{
    local_name, namespace_url, ns, parse_fragment,
    serialize::{serialize, Serialize, SerializeOpts, Serializer, TraversalScope},
    tendril::{StrTendril, TendrilSink},
    tree_builder::{NodeOrText, TreeSink},
    Attribute, ParseOpts, QualName,
};
use tracing::debug;

/// An HTML fragment.
///
/// To get the serialized HTML, use its `Display` implementation.
#[derive(Debug)]
pub struct Fragment {
    pub nodes: Vec<Node>,
}

impl Fragment {
    /// Construct a new `Fragment` by parsing the given HTML.
    pub fn parse_html(html: &str) -> Self {
        let sink = Self::default();
        let mut parser = parse_fragment(
            sink,
            ParseOpts::default(),
            QualName::new(None, ns!(html), local_name!("div")),
            Vec::new(),
        );
        parser.process(html.into());
        parser.finish()
    }

    /// Construct a new `Node` with the given data and add it to this `Fragment`.
    ///
    /// Returns the index of the new node.
    pub fn new_node(&mut self, data: NodeData) -> usize {
        self.nodes.push(Node::new(data));
        self.nodes.len() - 1
    }

    /// Append the given node to the given parent in this `Fragment`.
    ///
    /// The node is detached from its previous position.
    pub fn append_node(&mut self, parent_id: usize, node_id: usize) {
        self.detach(node_id);

        self.nodes[node_id].parent = Some(parent_id);
        if let Some(last_child) = self.nodes[parent_id].last_child.take() {
            self.nodes[node_id].prev_sibling = Some(last_child);
            self.nodes[last_child].next_sibling = Some(node_id);
        } else {
            self.nodes[parent_id].first_child = Some(node_id);
        }
        self.nodes[parent_id].last_child = Some(node_id);
    }

    /// Insert the given node before the given sibling in this `Fragment`.
    ///
    /// The node is detached from its previous position.
    pub fn insert_before(&mut self, sibling_id: usize, node_id: usize) {
        self.detach(node_id);

        self.nodes[node_id].parent = self.nodes[sibling_id].parent;
        self.nodes[node_id].next_sibling = Some(sibling_id);
        if let Some(prev_sibling) = self.nodes[sibling_id].prev_sibling.take() {
            self.nodes[node_id].prev_sibling = Some(prev_sibling);
            self.nodes[prev_sibling].next_sibling = Some(node_id);
        } else if let Some(parent) = self.nodes[sibling_id].parent {
            self.nodes[parent].first_child = Some(node_id);
        }
        self.nodes[sibling_id].prev_sibling = Some(node_id);
    }

    /// Detach the given node from this `Fragment`.
    pub fn detach(&mut self, node_id: usize) {
        let (parent, prev_sibling, next_sibling) = {
            let node = &mut self.nodes[node_id];
            (node.parent.take(), node.prev_sibling.take(), node.next_sibling.take())
        };

        if let Some(next_sibling) = next_sibling {
            self.nodes[next_sibling].prev_sibling = prev_sibling;
        } else if let Some(parent) = parent {
            self.nodes[parent].last_child = prev_sibling;
        }

        if let Some(prev_sibling) = prev_sibling {
            self.nodes[prev_sibling].next_sibling = next_sibling;
        } else if let Some(parent) = parent {
            self.nodes[parent].first_child = next_sibling;
        }
    }
}

impl Default for Fragment {
    fn default() -> Self {
        Self { nodes: vec![Node::new(NodeData::Document)] }
    }
}

impl TreeSink for Fragment {
    type Handle = usize;
    type Output = Self;

    fn finish(self) -> Self::Output {
        self
    }

    fn parse_error(&mut self, msg: std::borrow::Cow<'static, str>) {
        debug!("HTML parse error: {msg}");
    }

    fn get_document(&mut self) -> Self::Handle {
        0
    }

    fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> html5ever::ExpandedName<'a> {
        self.nodes[*target].as_element().expect("not an element").name.expanded()
    }

    fn create_element(
        &mut self,
        name: QualName,
        attrs: Vec<Attribute>,
        _flags: html5ever::tree_builder::ElementFlags,
    ) -> Self::Handle {
        self.new_node(NodeData::Element(ElementData { name, attrs: attrs.into_iter().collect() }))
    }

    fn create_comment(&mut self, _text: StrTendril) -> Self::Handle {
        self.new_node(NodeData::Other)
    }

    fn create_pi(&mut self, _target: StrTendril, _data: StrTendril) -> Self::Handle {
        self.new_node(NodeData::Other)
    }

    fn append(&mut self, parent: &Self::Handle, child: NodeOrText<Self::Handle>) {
        match child {
            NodeOrText::AppendNode(index) => self.append_node(*parent, index),
            NodeOrText::AppendText(text) => {
                // If the previous sibling is also text, add this text to it.
                if let Some(sibling) =
                    self.nodes[*parent].last_child.and_then(|child| self.nodes[child].as_text_mut())
                {
                    sibling.push_tendril(&text);
                } else {
                    let index = self.new_node(NodeData::Text(text));
                    self.append_node(*parent, index);
                }
            }
        }
    }

    fn append_based_on_parent_node(
        &mut self,
        element: &Self::Handle,
        prev_element: &Self::Handle,
        child: NodeOrText<Self::Handle>,
    ) {
        if self.nodes[*element].parent.is_some() {
            self.append_before_sibling(element, child);
        } else {
            self.append(prev_element, child);
        }
    }

    fn append_doctype_to_document(
        &mut self,
        _name: StrTendril,
        _public_id: StrTendril,
        _system_id: StrTendril,
    ) {
    }

    fn get_template_contents(&mut self, target: &Self::Handle) -> Self::Handle {
        *target
    }

    fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool {
        x == y
    }

    fn set_quirks_mode(&mut self, _mode: html5ever::tree_builder::QuirksMode) {}

    fn append_before_sibling(
        &mut self,
        sibling: &Self::Handle,
        new_node: NodeOrText<Self::Handle>,
    ) {
        match new_node {
            NodeOrText::AppendNode(index) => self.insert_before(*sibling, index),
            NodeOrText::AppendText(text) => {
                // If the previous sibling is also text, add this text to it.
                if let Some(prev_text) = self.nodes[*sibling]
                    .prev_sibling
                    .and_then(|prev| self.nodes[prev].as_text_mut())
                {
                    prev_text.push_tendril(&text);
                } else {
                    let index = self.new_node(NodeData::Text(text));
                    self.insert_before(*sibling, index);
                }
            }
        }
    }

    fn add_attrs_if_missing(&mut self, target: &Self::Handle, attrs: Vec<Attribute>) {
        let target = self.nodes[*target].as_element_mut().unwrap();
        target.attrs.extend(attrs.into_iter());
    }

    fn remove_from_parent(&mut self, target: &Self::Handle) {
        self.detach(*target);
    }

    fn reparent_children(&mut self, node: &Self::Handle, new_parent: &Self::Handle) {
        let mut next_child = self.nodes[*node].first_child;
        while let Some(child) = next_child {
            next_child = self.nodes[child].next_sibling;
            self.append_node(*new_parent, child);
        }
    }
}

impl Serialize for Fragment {
    fn serialize<S>(&self, serializer: &mut S, traversal_scope: TraversalScope) -> io::Result<()>
    where
        S: Serializer,
    {
        match traversal_scope {
            TraversalScope::IncludeNode => {
                let root = self.nodes[0].first_child.unwrap();

                let mut next_child = self.nodes[root].first_child;
                while let Some(child) = next_child {
                    let child = &self.nodes[child];
                    child.serialize(self, serializer)?;
                    next_child = child.next_sibling;
                }

                Ok(())
            }
            TraversalScope::ChildrenOnly(_) => Ok(()),
        }
    }
}

impl fmt::Display for Fragment {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut u8_vec = Vec::new();
        serialize(
            &mut u8_vec,
            self,
            SerializeOpts { traversal_scope: TraversalScope::IncludeNode, ..Default::default() },
        )
        .unwrap();

        f.write_str(&String::from_utf8(u8_vec).unwrap())?;

        Ok(())
    }
}

/// An HTML node.
#[derive(Debug)]
pub struct Node {
    pub parent: Option<usize>,
    pub prev_sibling: Option<usize>,
    pub next_sibling: Option<usize>,
    pub first_child: Option<usize>,
    pub last_child: Option<usize>,
    pub data: NodeData,
}

impl Node {
    /// Constructs a new `Node` with the given data.
    pub fn new(data: NodeData) -> Self {
        Self {
            parent: None,
            prev_sibling: None,
            next_sibling: None,
            first_child: None,
            last_child: None,
            data,
        }
    }

    /// Returns the `ElementData` of this `Node` if it is a `NodeData::Element`.
    pub fn as_element(&self) -> Option<&ElementData> {
        match &self.data {
            NodeData::Element(data) => Some(data),
            _ => None,
        }
    }

    /// Returns the mutable `ElementData` of this `Node` if it is a `NodeData::Element`.
    pub fn as_element_mut(&mut self) -> Option<&mut ElementData> {
        match &mut self.data {
            NodeData::Element(data) => Some(data),
            _ => None,
        }
    }

    /// Returns the mutable text content of this `Node`, if it is a `NodeData::Text`.
    pub fn as_text_mut(&mut self) -> Option<&mut StrTendril> {
        match &mut self.data {
            NodeData::Text(data) => Some(data),
            _ => None,
        }
    }
}

impl Node {
    pub fn serialize<S>(&self, fragment: &Fragment, serializer: &mut S) -> io::Result<()>
    where
        S: Serializer,
    {
        match &self.data {
            NodeData::Element(ref data) => {
                serializer.start_elem(
                    data.name.clone(),
                    data.attrs.iter().map(|attr| (&attr.name, &*attr.value)),
                )?;

                let mut next_child = self.first_child;
                while let Some(child) = next_child {
                    let child = &fragment.nodes[child];
                    child.serialize(fragment, serializer)?;
                    next_child = child.next_sibling;
                }

                serializer.end_elem(data.name.clone())?;

                Ok(())
            }
            NodeData::Document => {
                let mut next_child = self.first_child;
                while let Some(child) = next_child {
                    let child = &fragment.nodes[child];
                    child.serialize(fragment, serializer)?;
                    next_child = child.next_sibling;
                }

                Ok(())
            }
            NodeData::Text(ref text) => serializer.write_text(text),
            _ => Ok(()),
        }
    }
}

/// The data of a `Node`.
#[derive(Debug)]
pub enum NodeData {
    /// The root node of the `Fragment`.
    Document,

    /// A text node.
    Text(StrTendril),

    /// An HTML element (aka a tag).
    Element(ElementData),

    /// Other types (comment, processing instruction, …).
    Other,
}

/// The data of an HTML element.
#[derive(Debug)]
pub struct ElementData {
    /// The qualified name of the element.
    pub name: QualName,

    /// The attributes of the element.
    pub attrs: BTreeSet<Attribute>,
}

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

    #[test]
    fn sanity() {
        let html = "\

Title
\

\

This is some text
\

\
        ";
        assert_eq!(Fragment::parse_html(html).to_string(), html);

        assert_eq!(Fragment::parse_html("").to_string(), "");
    }
}
<commit_msg>events: Remove unnecessary refs<commit_after>use std::{collections::BTreeSet, fmt, io};

use html5ever::{
    local_name, namespace_url, ns, parse_fragment,
    serialize::{serialize, Serialize, SerializeOpts, Serializer, TraversalScope},
    tendril::{StrTendril, TendrilSink},
    tree_builder::{NodeOrText, TreeSink},
    Attribute, ParseOpts, QualName,
};
use tracing::debug;

/// An HTML fragment.
///
/// To get the serialized HTML, use its `Display` implementation.
#[derive(Debug)]
pub struct Fragment {
    pub nodes: Vec<Node>,
}

impl Fragment {
    /// Construct a new `Fragment` by parsing the given HTML.
    pub fn parse_html(html: &str) -> Self {
        let sink = Self::default();
        let mut parser = parse_fragment(
            sink,
            ParseOpts::default(),
            QualName::new(None, ns!(html), local_name!("div")),
            Vec::new(),
        );
        parser.process(html.into());
        parser.finish()
    }

    /// Construct a new `Node` with the given data and add it to this `Fragment`.
    ///
    /// Returns the index of the new node.
    pub fn new_node(&mut self, data: NodeData) -> usize {
        self.nodes.push(Node::new(data));
        self.nodes.len() - 1
    }

    /// Append the given node to the given parent in this `Fragment`.
    ///
    /// The node is detached from its previous position.
    pub fn append_node(&mut self, parent_id: usize, node_id: usize) {
        self.detach(node_id);

        self.nodes[node_id].parent = Some(parent_id);
        if let Some(last_child) = self.nodes[parent_id].last_child.take() {
            self.nodes[node_id].prev_sibling = Some(last_child);
            self.nodes[last_child].next_sibling = Some(node_id);
        } else {
            self.nodes[parent_id].first_child = Some(node_id);
        }
        self.nodes[parent_id].last_child = Some(node_id);
    }

    /// Insert the given node before the given sibling in this `Fragment`.
    ///
    /// The node is detached from its previous position.
    pub fn insert_before(&mut self, sibling_id: usize, node_id: usize) {
        self.detach(node_id);

        self.nodes[node_id].parent = self.nodes[sibling_id].parent;
        self.nodes[node_id].next_sibling = Some(sibling_id);
        if let Some(prev_sibling) = self.nodes[sibling_id].prev_sibling.take() {
            self.nodes[node_id].prev_sibling = Some(prev_sibling);
            self.nodes[prev_sibling].next_sibling = Some(node_id);
        } else if let Some(parent) = self.nodes[sibling_id].parent {
            self.nodes[parent].first_child = Some(node_id);
        }
        self.nodes[sibling_id].prev_sibling = Some(node_id);
    }

    /// Detach the given node from this `Fragment`.
    pub fn detach(&mut self, node_id: usize) {
        let (parent, prev_sibling, next_sibling) = {
            let node = &mut self.nodes[node_id];
            (node.parent.take(), node.prev_sibling.take(), node.next_sibling.take())
        };

        if let Some(next_sibling) = next_sibling {
            self.nodes[next_sibling].prev_sibling = prev_sibling;
        } else if let Some(parent) = parent {
            self.nodes[parent].last_child = prev_sibling;
        }

        if let Some(prev_sibling) = prev_sibling {
            self.nodes[prev_sibling].next_sibling = next_sibling;
        } else if let Some(parent) = parent {
            self.nodes[parent].first_child = next_sibling;
        }
    }
}

impl Default for Fragment {
    fn default() -> Self {
        Self { nodes: vec![Node::new(NodeData::Document)] }
    }
}

impl TreeSink for Fragment {
    type Handle = usize;
    type Output = Self;

    fn finish(self) -> Self::Output {
        self
    }

    fn parse_error(&mut self, msg: std::borrow::Cow<'static, str>) {
        debug!("HTML parse error: {msg}");
    }

    fn get_document(&mut self) -> Self::Handle {
        0
    }

    fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> html5ever::ExpandedName<'a> {
        self.nodes[*target].as_element().expect("not an element").name.expanded()
    }

    fn create_element(
        &mut self,
        name: QualName,
        attrs: Vec<Attribute>,
        _flags: html5ever::tree_builder::ElementFlags,
    ) -> Self::Handle {
        self.new_node(NodeData::Element(ElementData { name, attrs: attrs.into_iter().collect() }))
    }

    fn create_comment(&mut self, _text: StrTendril) -> Self::Handle {
        self.new_node(NodeData::Other)
    }

    fn create_pi(&mut self, _target: StrTendril, _data: StrTendril) -> Self::Handle {
        self.new_node(NodeData::Other)
    }

    fn append(&mut self, parent: &Self::Handle, child: NodeOrText<Self::Handle>) {
        match child {
            NodeOrText::AppendNode(index) => self.append_node(*parent, index),
            NodeOrText::AppendText(text) => {
                // If the previous sibling is also text, add this text to it.
                if let Some(sibling) =
                    self.nodes[*parent].last_child.and_then(|child| self.nodes[child].as_text_mut())
                {
                    sibling.push_tendril(&text);
                } else {
                    let index = self.new_node(NodeData::Text(text));
                    self.append_node(*parent, index);
                }
            }
        }
    }

    fn append_based_on_parent_node(
        &mut self,
        element: &Self::Handle,
        prev_element: &Self::Handle,
        child: NodeOrText<Self::Handle>,
    ) {
        if self.nodes[*element].parent.is_some() {
            self.append_before_sibling(element, child);
        } else {
            self.append(prev_element, child);
        }
    }

    fn append_doctype_to_document(
        &mut self,
        _name: StrTendril,
        _public_id: StrTendril,
        _system_id: StrTendril,
    ) {
    }

    fn get_template_contents(&mut self, target: &Self::Handle) -> Self::Handle {
        *target
    }

    fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool {
        x == y
    }

    fn set_quirks_mode(&mut self, _mode: html5ever::tree_builder::QuirksMode) {}

    fn append_before_sibling(
        &mut self,
        sibling: &Self::Handle,
        new_node: NodeOrText<Self::Handle>,
    ) {
        match new_node {
            NodeOrText::AppendNode(index) => self.insert_before(*sibling, index),
            NodeOrText::AppendText(text) => {
                // If the previous sibling is also text, add this text to it.
                if let Some(prev_text) = self.nodes[*sibling]
                    .prev_sibling
                    .and_then(|prev| self.nodes[prev].as_text_mut())
                {
                    prev_text.push_tendril(&text);
                } else {
                    let index = self.new_node(NodeData::Text(text));
                    self.insert_before(*sibling, index);
                }
            }
        }
    }

    fn add_attrs_if_missing(&mut self, target: &Self::Handle, attrs: Vec<Attribute>) {
        let target = self.nodes[*target].as_element_mut().unwrap();
        target.attrs.extend(attrs.into_iter());
    }

    fn remove_from_parent(&mut self, target: &Self::Handle) {
        self.detach(*target);
    }

    fn reparent_children(&mut self, node: &Self::Handle, new_parent: &Self::Handle) {
        let mut next_child = self.nodes[*node].first_child;
        while let Some(child) = next_child {
            next_child = self.nodes[child].next_sibling;
            self.append_node(*new_parent, child);
        }
    }
}

impl Serialize for Fragment {
    fn serialize<S>(&self, serializer: &mut S, traversal_scope: TraversalScope) -> io::Result<()>
    where
        S: Serializer,
    {
        match traversal_scope {
            TraversalScope::IncludeNode => {
                let root = self.nodes[0].first_child.unwrap();

                let mut next_child = self.nodes[root].first_child;
                while let Some(child) = next_child {
                    let child = &self.nodes[child];
                    child.serialize(self, serializer)?;
                    next_child = child.next_sibling;
                }

                Ok(())
            }
            TraversalScope::ChildrenOnly(_) => Ok(()),
        }
    }
}

impl fmt::Display for Fragment {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut u8_vec = Vec::new();
        serialize(
            &mut u8_vec,
            self,
            SerializeOpts { traversal_scope: TraversalScope::IncludeNode, ..Default::default() },
        )
        .unwrap();

        f.write_str(&String::from_utf8(u8_vec).unwrap())?;

        Ok(())
    }
}

/// An HTML node.
#[derive(Debug)]
pub struct Node {
    pub parent: Option<usize>,
    pub prev_sibling: Option<usize>,
    pub next_sibling: Option<usize>,
    pub first_child: Option<usize>,
    pub last_child: Option<usize>,
    pub data: NodeData,
}

impl Node {
    /// Constructs a new `Node` with the given data.
    pub fn new(data: NodeData) -> Self {
        Self {
            parent: None,
            prev_sibling: None,
            next_sibling: None,
            first_child: None,
            last_child: None,
            data,
        }
    }

    /// Returns the `ElementData` of this `Node` if it is a `NodeData::Element`.
    pub fn as_element(&self) -> Option<&ElementData> {
        match &self.data {
            NodeData::Element(data) => Some(data),
            _ => None,
        }
    }

    /// Returns the mutable `ElementData` of this `Node` if it is a `NodeData::Element`.
    pub fn as_element_mut(&mut self) -> Option<&mut ElementData> {
        match &mut self.data {
            NodeData::Element(data) => Some(data),
            _ => None,
        }
    }

    /// Returns the mutable text content of this `Node`, if it is a `NodeData::Text`.
    pub fn as_text_mut(&mut self) -> Option<&mut StrTendril> {
        match &mut self.data {
            NodeData::Text(data) => Some(data),
            _ => None,
        }
    }
}

impl Node {
    pub fn serialize<S>(&self, fragment: &Fragment, serializer: &mut S) -> io::Result<()>
    where
        S: Serializer,
    {
        match &self.data {
            NodeData::Element(data) => {
                serializer.start_elem(
                    data.name.clone(),
                    data.attrs.iter().map(|attr| (&attr.name, &*attr.value)),
                )?;

                let mut next_child = self.first_child;
                while let Some(child) = next_child {
                    let child = &fragment.nodes[child];
                    child.serialize(fragment, serializer)?;
                    next_child = child.next_sibling;
                }

                serializer.end_elem(data.name.clone())?;

                Ok(())
            }
            NodeData::Document => {
                let mut next_child = self.first_child;
                while let Some(child) = next_child {
                    let child = &fragment.nodes[child];
                    child.serialize(fragment, serializer)?;
                    next_child = child.next_sibling;
                }

                Ok(())
            }
            NodeData::Text(text) => serializer.write_text(text),
            _ => Ok(()),
        }
    }
}

/// The data of a `Node`.
#[derive(Debug)]
pub enum NodeData {
    /// The root node of the `Fragment`.
    Document,

    /// A text node.
    Text(StrTendril),

    /// An HTML element (aka a tag).
    Element(ElementData),

    /// Other types (comment, processing instruction, …).
    Other,
}

/// The data of an HTML element.
#[derive(Debug)]
pub struct ElementData {
    /// The qualified name of the element.
    pub name: QualName,

    /// The attributes of the element.
    pub attrs: BTreeSet<Attribute>,
}

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

    #[test]
    fn sanity() {
        let html = "\

Title
\

\

This is some text
\

\
        ";
        assert_eq!(Fragment::parse_html(html).to_string(), html);

        assert_eq!(Fragment::parse_html("").to_string(), "");
    }
}
<|endoftext|>


<commit_before>//! A generic circular ring buffer.
//!
//! A ring buffer can continue appending data to itself indefinitely.  However,
//! it has a limited capacity; when that capacity is reached, it will overwrite
//! the oldest data to make space.

use std::iter::Zip;
use std::ops::{Index, IndexMut};
use std::vec::Vec;

use types::Time;

/// A generic ring buffer.
#[derive(Clone, Debug)]
pub struct RingBuffer<T> {
    buf: Vec<T>,
    capacity: usize,
    size: usize,
    start_t: Time,
    end_t: Time,
}

impl<T> RingBuffer<T> {
    /// Return an empty ring buffer that can hold at most `capacity` elements.
    pub fn new(capacity: usize) -> Self {
        RingBuffer {
            buf: Vec::with_capacity(capacity),
            capacity: capacity,
            size: 0,
            start_t: 0,
            end_t: 0
        }
    }

    /// Returns the number of elements the ringbuffer currently contains.
    pub fn len(&self) -> usize {
        (self.end_t - self.start_t) as usize
    }

    /// Returns the number of elements the ringbuffer can hold at one time.
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Returns the range of timestamps stored in the ringbuffer, as a tuple
    /// (first, last). First is inclusive, while last is exlusive.
    pub fn times(&self) -> (Time, Time) {
        (self.start_t, self.end_t)
    }

    /// Attempt to return a ref to the data stored at time `t`. If the requested
    /// time is not in the buffer, instead returns `None`.
    pub fn get(&self, t: Time) -> Option<&T> {
        if self.start_t <= t && t < self.end_t {
            Some(&self[t])
        } else {
            None
        }
    }

    /// Attempt to return a mutable ref to the data stored at time `t`. If the
    /// requested time is not in the buffer, instead returns `None`.
    pub fn get_mut(&mut self, t: Time) -> Option<&mut T> {
        if self.start_t <= t && t < self.end_t {
            Some(&mut self[t])
        } else {
            None
        }
    }

    /// Push the supplied data onto the end of the buffer. If the buffer is
    /// full, this will overwrite the oldest data.
    pub fn push(&mut self, data: T) {
        if self.size < self.capacity {
            self.buf.push(data);
            self.size += 1;
            self.end_t += 1;
        } else {
            self.buf[(self.end_t % self.capacity as Time) as usize] = data;
            self.start_t += 1;
            self.end_t += 1;
        }
    }

    /// Clear all the elements from the buffer.
    pub fn clear(&mut self) {
        self.start_t = self.end_t;
    }

    /// Returns an iterator over the buffer.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let values: Vec<_> = rb.iter().collect();
    /// assert_eq!(values, [&"second", &"third"]);
    /// ```
    pub fn iter(&self) -> Iter<T> {
        Iter { buffer: self, t: self.start_t }
    }

    /// Returns an iterator over the buffer's timestamps.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let times: Vec<_> = rb.timestamp_iter().collect();
    /// assert_eq!(times, [1, 2]);
    /// ```
    pub fn timestamp_iter(&self) -> IterTimes {
        IterTimes { t: self.start_t, end: self.end_t }
    }

    /// Returns an iterator that pairs values with their timestamp.
    ///
    /// Equivalent to `self.timestamp_iter().zip(self.iter())`.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let zipped: Vec<_> = rb.zipped_iter().collect();
    /// assert_eq!(zipped, [(1, &"second"), (2, &"third")]);
    /// ```
    pub fn zipped_iter(&self) -> Zip<IterTimes, Iter<T>> {
        self.timestamp_iter().zip(self.iter())
    }
}

impl<T> Index<Time> for RingBuffer<T> {
    type Output = T;
    fn index(&self, t: Time) -> &T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        &self.buf[(t % self.capacity as Time) as usize]
    }
}

impl<T> IndexMut<Time> for RingBuffer<T> {
    fn index_mut(&mut self, t: Time) -> &mut T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        &mut self.buf[(t % self.capacity as Time) as usize]
    }
}

/// An iterator over RingBuffer values.
pub struct Iter<'a, T: 'a> {
    buffer: &'a RingBuffer<T>,
    t: Time
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.buffer.end_t {
            None
        } else {
            let result = &self.buffer[self.t];
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.buffer.end_t - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

/// An iterator over RingBuffer timestamps.
pub struct IterTimes {
    t: Time,
    end: Time
}

impl Iterator for IterTimes {
    type Item = Time;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.end {
            None
        } else {
            let result = self.t;
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.end - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

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

    fn get_test_rb() -> RingBuffer<i32> {
        RingBuffer {
            buf: vec![7,13],
            capacity: 2,
            size: 2,
            start_t: 7,
            end_t: 9
        }
    }

    #[test]
    fn test_push() {
        let mut rb = RingBuffer::<i32>::new(2);

        rb.push(13);
        assert_eq!(rb.size, 1);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 1);
        assert_eq!(rb.buf[0], 13);

        rb.push(7);
        assert_eq!(rb.size, 2);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 2);
        assert_eq!(rb.buf[0], 13);
        assert_eq!(rb.buf[1], 7);

        rb.push(3);
        assert_eq!(rb.size, 2);
        assert_eq!(rb.start_t, 1);
        assert_eq!(rb.end_t, 3);
        assert_eq!(rb.buf[0], 3);
        assert_eq!(rb.buf[1], 7);
    }

    #[test]
    fn test_get() {
        // get is just a wrapper around Index, so just do some simple black box
        // testing; glass box is handled in test_index.
        let rb = get_test_rb();
        assert_eq!(rb.get(6), None);
        assert_eq!(rb.get(7), Some(&13));
        assert_eq!(rb.get(8), Some(&7));
        assert_eq!(rb.get(9), None);
    }

    #[test]
    fn test_get_mut() {
        // get_mut is just a wrapper around IndexMut, so just do some simple
        // black box testing; glass box is handled in test_index_mut.
        let mut rb = get_test_rb();
        assert_eq!(rb.get_mut(6), None);
        assert_eq!(rb.get_mut(7), Some(&mut 13));
        assert_eq!(rb.get_mut(8), Some(&mut 7));
        assert_eq!(rb.get_mut(9), None);
    }

    #[test]
    fn test_index() {
        let mut rb = get_test_rb();

        // Test with odd start
        assert_eq!(rb[7], 13);
        assert_eq!(rb[8], 7);

        // Test with even start
        rb.start_t = 6; rb.end_t = 8;
        assert_eq!(rb[6], 7);
        assert_eq!(rb[7], 13);
    }

    #[test]
    #[should_panic]
    fn test_index_under() {
        let rb = get_test_rb();
        rb[6];
    }

    #[test]
    #[should_panic]
    fn test_index_over() {
        let rb = get_test_rb();
        rb[9];
    }

    #[test]
    fn test_index_mut() {
        let mut rb = get_test_rb();

        // Test in range
        rb[7] = 22;
        rb[8] = 23;
        assert_eq!(rb.buf[1], 22);
        assert_eq!(rb.buf[0], 23);
    }

    #[test]
    #[should_panic]
    fn test_index_mut_under() {
        let mut rb = get_test_rb();
        rb[6] = 3;
    }

    #[test]
    #[should_panic]
    fn test_index_mut_over() {
        let mut rb = get_test_rb();
        rb[9] = 3;
    }
}
<commit_msg>Allow resizing ringbuffers<commit_after>//! A generic circular ring buffer.
//!
//! A ring buffer can continue appending data to itself indefinitely.  However,
//! it has a limited capacity; when that capacity is reached, it will overwrite
//! the oldest data to make space.

use std::iter::Zip;
use std::ops::{Index, IndexMut};
use std::vec::Vec;

use types::Time;

/// A generic ring buffer.
#[derive(Clone, Debug)]
pub struct RingBuffer<T> {
    buf: Vec<T>,
    capacity: usize,
    start_i: usize,
    start_t: Time,
    end_t: Time,
}

impl<T> RingBuffer<T> {
    /// Return an empty ring buffer that can hold at most `capacity` elements.
    pub fn new(capacity: usize) -> Self {
        RingBuffer {
            buf: Vec::with_capacity(capacity),
            start_i: 0,
            capacity: capacity,
            start_t: 0,
            end_t: 0
        }
    }

    /// Returns the number of elements the ringbuffer currently contains.
    pub fn len(&self) -> usize {
        (self.end_t - self.start_t) as usize
    }

    /// Returns the number of elements the ringbuffer can hold at one time.
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Returns the range of timestamps stored in the ringbuffer, as a tuple
    /// (first, last). First is inclusive, while last is exlusive.
    pub fn times(&self) -> (Time, Time) {
        (self.start_t, self.end_t)
    }

    /// Attempt to return a ref to the data stored at time `t`. If the requested
    /// time is not in the buffer, instead returns `None`.
    pub fn get(&self, t: Time) -> Option<&T> {
        if self.start_t <= t && t < self.end_t {
            Some(&self[t])
        } else {
            None
        }
    }

    /// Attempt to return a mutable ref to the data stored at time `t`. If the
    /// requested time is not in the buffer, instead returns `None`.
    pub fn get_mut(&mut self, t: Time) -> Option<&mut T> {
        if self.start_t <= t && t < self.end_t {
            Some(&mut self[t])
        } else {
            None
        }
    }

    /// Push the supplied data onto the end of the buffer. If the buffer is
    /// full, this will overwrite the oldest data.
    pub fn push(&mut self, data: T) {
        if self.buf.len() < self.capacity {
            self.buf.push(data);
            self.end_t += 1;
        } else {
            let i = self.start_i + (self.end_t - self.start_t) as usize;
            self.buf[i % self.capacity] = data;
            self.start_i = (self.start_i+1) % self.capacity;
            self.start_t += 1;
            self.end_t += 1;
        }
    }

    /// Clear all the elements from the buffer.
    pub fn clear(&mut self) {
        self.start_t = self.end_t;
    }

    /// Returns an iterator over the buffer.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let values: Vec<_> = rb.iter().collect();
    /// assert_eq!(values, [&"second", &"third"]);
    /// ```
    pub fn iter(&self) -> Iter<T> {
        Iter { buffer: self, t: self.start_t }
    }

    /// Returns an iterator over the buffer's timestamps.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let times: Vec<_> = rb.timestamp_iter().collect();
    /// assert_eq!(times, [1, 2]);
    /// ```
    pub fn timestamp_iter(&self) -> IterTimes {
        IterTimes { t: self.start_t, end: self.end_t }
    }

    /// Returns an iterator that pairs values with their timestamp.
    ///
    /// Equivalent to `self.timestamp_iter().zip(self.iter())`.
    ///
    /// # Example
    ///
    /// ```
    /// # use oxcable::utils::ringbuffer::RingBuffer;
    /// let mut rb = RingBuffer::new(2);
    /// rb.push("first");
    /// rb.push("second");
    /// rb.push("third");
    ///
    /// let zipped: Vec<_> = rb.zipped_iter().collect();
    /// assert_eq!(zipped, [(1, &"second"), (2, &"third")]);
    /// ```
    pub fn zipped_iter(&self) -> Zip<IterTimes, Iter<T>> {
        self.timestamp_iter().zip(self.iter())
    }
}

impl<T: Clone> RingBuffer<T> {
    /// Resize the ringbuffer to hold up to `capacity` elements. If the new
    /// capacity is smaller than the old one, then the oldest elements will be
    /// removed from the buffer.
    pub fn resize(&mut self, capacity: usize) {
        let mut elems = (self.end_t - self.start_t) as usize;
        if capacity < elems {
            let drops = elems - capacity;
            self.start_i += drops;
            self.start_t += drops as Time;
            elems = capacity;
        }

        let mut new_buf = Vec::with_capacity(capacity);
        for i in self.start_i..(self.start_i+elems) {
            new_buf.push(self.buf[i % self.capacity].clone());
        }
        self.buf = new_buf;

        self.start_i = 0;
        self.capacity = capacity;
    }
}

impl<T> Index<Time> for RingBuffer<T> {
    type Output = T;
    fn index(&self, t: Time) -> &T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &self.buf[i % self.capacity]
    }
}

impl<T> IndexMut<Time> for RingBuffer<T> {
    fn index_mut(&mut self, t: Time) -> &mut T {
        if t < self.start_t || t >= self.end_t {
            panic!("index out of bounds: buffer has range [{},{}), but index is {}",
                self.start_t, self.end_t, t);
        }
        let i = self.start_i + (t - self.start_t) as usize;
        &mut self.buf[i % self.capacity]
    }
}

/// An iterator over RingBuffer values.
pub struct Iter<'a, T: 'a> {
    buffer: &'a RingBuffer<T>,
    t: Time
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.buffer.end_t {
            None
        } else {
            let result = &self.buffer[self.t];
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.buffer.end_t - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

/// An iterator over RingBuffer timestamps.
pub struct IterTimes {
    t: Time,
    end: Time
}

impl Iterator for IterTimes {
    type Item = Time;
    fn next(&mut self) -> Option<Self::Item> {
        if self.t == self.end {
            None
        } else {
            let result = self.t;
            self.t += 1;
            Some(result)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let exact = (self.end - self.t) as usize;
        (exact, Some(exact))
    }

    fn count(self) -> usize {
        self.size_hint().0
    }
}

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

    fn get_test_rb() -> RingBuffer<i32> {
        RingBuffer {
            buf: vec![7,13],
            capacity: 2,
            start_i: 1,
            start_t: 7,
            end_t: 9
        }
    }

    #[test]
    fn test_push() {
        let mut rb = RingBuffer::<i32>::new(2);

        rb.push(13);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 1);
        assert_eq!(rb.buf[0], 13);

        rb.push(7);
        assert_eq!(rb.start_t, 0);
        assert_eq!(rb.end_t, 2);
        assert_eq!(rb.buf[0], 13);
        assert_eq!(rb.buf[1], 7);

        rb.push(3);
        assert_eq!(rb.start_t, 1);
        assert_eq!(rb.end_t, 3);
        assert_eq!(rb.buf[0], 3);
        assert_eq!(rb.buf[1], 7);
    }

    #[test]
    fn test_get() {
        // get is just a wrapper around Index, so just do some simple black box
        // testing; glass box is handled in test_index.
        let rb = get_test_rb();
        assert_eq!(rb.get(6), None);
        assert_eq!(rb.get(7), Some(&13));
        assert_eq!(rb.get(8), Some(&7));
        assert_eq!(rb.get(9), None);
    }

    #[test]
    fn test_get_mut() {
        // get_mut is just a wrapper around IndexMut, so just do some simple
        // black box testing; glass box is handled in test_index_mut.
        let mut rb = get_test_rb();
        assert_eq!(rb.get_mut(6), None);
        assert_eq!(rb.get_mut(7), Some(&mut 13));
        assert_eq!(rb.get_mut(8), Some(&mut 7));
        assert_eq!(rb.get_mut(9), None);
    }

    #[test]
    fn test_index() {
        let mut rb = get_test_rb();

        // Test with odd start
        assert_eq!(rb[7], 13);
        assert_eq!(rb[8], 7);

        // Test with even start
        rb.start_i = 0; rb.start_t = 6; rb.end_t = 8;
        assert_eq!(rb[6], 7);
        assert_eq!(rb[7], 13);
    }

    #[test]
    #[should_panic]
    fn test_index_under() {
        let rb = get_test_rb();
        rb[6];
    }

    #[test]
    #[should_panic]
    fn test_index_over() {
        let rb = get_test_rb();
        rb[9];
    }

    #[test]
    fn test_index_mut() {
        let mut rb = get_test_rb();

        rb[7] = 22;
        rb[8] = 23;
        assert_eq!(rb.buf[1], 22);
        assert_eq!(rb.buf[0], 23);
    }

    #[test]
    #[should_panic]
    fn test_index_mut_under() {
        let mut rb = get_test_rb();
        rb[6] = 3;
    }

    #[test]
    #[should_panic]
    fn test_index_mut_over() {
        let mut rb = get_test_rb();
        rb[9] = 3;
    }

    #[test]
    fn test_resize() {
        // Test expanding
        let mut rb = get_test_rb();
        rb.resize(4);
        rb.push(19);
        rb.push(21);
        rb.push(23);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [7,19,21,23]);

        // Test contracting
        rb.resize(2);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [21,23]);

        // Test expanding a partially full buffer
        let mut rb = RingBuffer::new(2);
        rb.push(1);
        rb.resize(4);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [1,2,3]);

        // Test contracting a partially full buffer
        let mut rb = RingBuffer::new(4);
        rb.push(1);
        rb.resize(2);
        rb.push(2);
        rb.push(3);
        assert_eq!(rb.iter().cloned().collect::<Vec<_>>(), [2,3]);
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Michael Gehring <mg@ebfe.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) delim mkdelim

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path;
use uucore::InvalidEncodingHandling;

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

static ABOUT: &str = "compare two sorted files line by line";
static LONG_HELP: &str = "";

mod options {
    pub const COLUMN_1: &str = "1";
    pub const COLUMN_2: &str = "2";
    pub const COLUMN_3: &str = "3";
    pub const DELIMITER: &str = "output-delimiter";
    pub const DELIMITER_DEFAULT: &str = "\t";
    pub const FILE_1: &str = "FILE1";
    pub const FILE_2: &str = "FILE2";
}

fn get_usage() -> String {
    format!("{} [OPTION]... FILE1 FILE2", executable!())
}

fn mkdelim(col: usize, opts: &ArgMatches) -> String {
    let mut s = String::new();
    let delim = opts.value_of(options::DELIMITER).unwrap();

    if col > 1 && !opts.is_present(options::COLUMN_1) {
        s.push_str(delim.as_ref());
    }
    if col > 2 && !opts.is_present(options::COLUMN_2) {
        s.push_str(delim.as_ref());
    }

    s
}

fn ensure_nl(line: &mut String) {
    match line.chars().last() {
        Some('\n') => (),
        _ => line.push('\n'),
    }
}

enum LineReader {
    Stdin(Stdin),
    FileIn(BufReader<File>),
}

impl LineReader {
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        match *self {
            LineReader::Stdin(ref mut r) => r.read_line(buf),
            LineReader::FileIn(ref mut r) => r.read_line(buf),
        }
    }
}

fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
    let delim: Vec<String> = (0..4).map(|col| mkdelim(col, opts)).collect();

    let ra = &mut String::new();
    let mut na = a.read_line(ra);
    let rb = &mut String::new();
    let mut nb = b.read_line(rb);

    while na.is_ok() || nb.is_ok() {
        let ord = match (na.is_ok(), nb.is_ok()) {
            (false, true) => Ordering::Greater,
            (true, false) => Ordering::Less,
            (true, true) => match (&na, &nb) {
                (&Ok(0), &Ok(0)) => break,
                (&Ok(0), _) => Ordering::Greater,
                (_, &Ok(0)) => Ordering::Less,
                _ => ra.cmp(&rb),
            },
            _ => unreachable!(),
        };

        match ord {
            Ordering::Less => {
                if !opts.is_present(options::COLUMN_1) {
                    ensure_nl(ra);
                    print!("{}{}", delim[1], ra);
                }
                ra.clear();
                na = a.read_line(ra);
            }
            Ordering::Greater => {
                if !opts.is_present(options::COLUMN_2) {
                    ensure_nl(rb);
                    print!("{}{}", delim[2], rb);
                }
                rb.clear();
                nb = b.read_line(rb);
            }
            Ordering::Equal => {
                if !opts.is_present(options::COLUMN_3) {
                    ensure_nl(ra);
                    print!("{}{}", delim[3], ra);
                }
                ra.clear();
                rb.clear();
                na = a.read_line(ra);
                nb = b.read_line(rb);
            }
        }
    }
}

fn open_file(name: &str) -> io::Result<LineReader> {
    match name {
        "-" => Ok(LineReader::Stdin(stdin())),
        _ => {
            let f = File::open(&Path::new(name))?;
            Ok(LineReader::FileIn(BufReader::new(f)))
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COLUMN_1)
                .short(options::COLUMN_1)
                .help("suppress column 1 (lines unique to FILE1)"),
        )
        .arg(
            Arg::with_name(options::COLUMN_2)
                .short(options::COLUMN_2)
                .help("suppress column 2 (lines unique to FILE2)"),
        )
        .arg(
            Arg::with_name(options::COLUMN_3)
                .short(options::COLUMN_3)
                .help("suppress column 3 (lines that appear in both files)"),
        )
        .arg(
            Arg::with_name(options::DELIMITER)
                .long(options::DELIMITER)
                .help("separate columns with STR")
                .value_name("STR")
                .default_value(options::DELIMITER_DEFAULT)
                .hide_default_value(true),
        )
        .arg(Arg::with_name(options::FILE_1).required(true))
        .arg(Arg::with_name(options::FILE_2).required(true))
        .get_matches_from(args);

    let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
    let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();

    comm(&mut f1, &mut f2, &matches);

    0
}
<commit_msg>comm: clean up line-end check<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Michael Gehring <mg@ebfe.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) delim mkdelim

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path;
use uucore::InvalidEncodingHandling;

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

static ABOUT: &str = "compare two sorted files line by line";
static LONG_HELP: &str = "";

mod options {
    pub const COLUMN_1: &str = "1";
    pub const COLUMN_2: &str = "2";
    pub const COLUMN_3: &str = "3";
    pub const DELIMITER: &str = "output-delimiter";
    pub const DELIMITER_DEFAULT: &str = "\t";
    pub const FILE_1: &str = "FILE1";
    pub const FILE_2: &str = "FILE2";
}

fn get_usage() -> String {
    format!("{} [OPTION]... FILE1 FILE2", executable!())
}

fn mkdelim(col: usize, opts: &ArgMatches) -> String {
    let mut s = String::new();
    let delim = opts.value_of(options::DELIMITER).unwrap();

    if col > 1 && !opts.is_present(options::COLUMN_1) {
        s.push_str(delim.as_ref());
    }
    if col > 2 && !opts.is_present(options::COLUMN_2) {
        s.push_str(delim.as_ref());
    }

    s
}

fn ensure_nl(line: &mut String) {
    if !line.ends_with('\n') {
        line.push('\n');
    }
}

enum LineReader {
    Stdin(Stdin),
    FileIn(BufReader<File>),
}

impl LineReader {
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        match *self {
            LineReader::Stdin(ref mut r) => r.read_line(buf),
            LineReader::FileIn(ref mut r) => r.read_line(buf),
        }
    }
}

fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
    let delim: Vec<String> = (0..4).map(|col| mkdelim(col, opts)).collect();

    let ra = &mut String::new();
    let mut na = a.read_line(ra);
    let rb = &mut String::new();
    let mut nb = b.read_line(rb);

    while na.is_ok() || nb.is_ok() {
        let ord = match (na.is_ok(), nb.is_ok()) {
            (false, true) => Ordering::Greater,
            (true, false) => Ordering::Less,
            (true, true) => match (&na, &nb) {
                (&Ok(0), &Ok(0)) => break,
                (&Ok(0), _) => Ordering::Greater,
                (_, &Ok(0)) => Ordering::Less,
                _ => ra.cmp(&rb),
            },
            _ => unreachable!(),
        };

        match ord {
            Ordering::Less => {
                if !opts.is_present(options::COLUMN_1) {
                    ensure_nl(ra);
                    print!("{}{}", delim[1], ra);
                }
                ra.clear();
                na = a.read_line(ra);
            }
            Ordering::Greater => {
                if !opts.is_present(options::COLUMN_2) {
                    ensure_nl(rb);
                    print!("{}{}", delim[2], rb);
                }
                rb.clear();
                nb = b.read_line(rb);
            }
            Ordering::Equal => {
                if !opts.is_present(options::COLUMN_3) {
                    ensure_nl(ra);
                    print!("{}{}", delim[3], ra);
                }
                ra.clear();
                rb.clear();
                na = a.read_line(ra);
                nb = b.read_line(rb);
            }
        }
    }
}

fn open_file(name: &str) -> io::Result<LineReader> {
    match name {
        "-" => Ok(LineReader::Stdin(stdin())),
        _ => {
            let f = File::open(&Path::new(name))?;
            Ok(LineReader::FileIn(BufReader::new(f)))
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COLUMN_1)
                .short(options::COLUMN_1)
                .help("suppress column 1 (lines unique to FILE1)"),
        )
        .arg(
            Arg::with_name(options::COLUMN_2)
                .short(options::COLUMN_2)
                .help("suppress column 2 (lines unique to FILE2)"),
        )
        .arg(
            Arg::with_name(options::COLUMN_3)
                .short(options::COLUMN_3)
                .help("suppress column 3 (lines that appear in both files)"),
        )
        .arg(
            Arg::with_name(options::DELIMITER)
                .long(options::DELIMITER)
                .help("separate columns with STR")
                .value_name("STR")
                .default_value(options::DELIMITER_DEFAULT)
                .hide_default_value(true),
        )
        .arg(Arg::with_name(options::FILE_1).required(true))
        .arg(Arg::with_name(options::FILE_2).required(true))
        .get_matches_from(args);

    let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
    let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();

    comm(&mut f1, &mut f2, &matches);

    0
}
<|endoftext|>


<commit_before>//* This file is part of the uutils coreutils package.
//*
//* (c) Roman Gafiyatullin <r.gafiyatullin@me.com>
//*
//* For the full copyright and license information, please view the LICENSE
//* file that was distributed with this source code.

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, USimpleError};
use uucore::InvalidEncodingHandling;

mod syntax_tree;
mod tokens;

const VERSION: &str = "version";
const HELP: &str = "help";

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .setting(AppSettings::InferLongArgs)
        .arg(Arg::new(VERSION).long(VERSION))
        .arg(Arg::new(HELP).long(HELP))
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    // For expr utility we do not want getopts.
    // The following usage should work without escaping hyphens: `expr -15 = 1 +  2 \* \( 3 - -4 \)`

    if maybe_handle_help_or_version(&args) {
        Ok(())
    } else {
        let token_strings = args[1..].to_vec();

        match process_expr(&token_strings) {
            Ok(expr_result) => print_expr_ok(&expr_result),
            Err(expr_error) => Err(USimpleError::new(2, &expr_error)),
        }
    }
}

fn process_expr(token_strings: &[String]) -> Result<String, String> {
    let maybe_tokens = tokens::strings_to_tokens(token_strings);
    let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens);
    evaluate_ast(maybe_ast)
}

fn print_expr_ok(expr_result: &str) -> UResult<()> {
    println!("{}", expr_result);
    if expr_result == "0" || expr_result.is_empty() {
        Err(1.into())
    } else {
        Ok(())
    }
}

fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
    maybe_ast.and_then(|ast| ast.evaluate())
}

fn maybe_handle_help_or_version(args: &[String]) -> bool {
    if args.len() == 2 {
        if args[1] == "--help" {
            print_help();
            true
        } else if args[1] == "--version" {
            print_version();
            true
        } else {
            false
        }
    } else {
        false
    }
}

fn print_help() {
    //! The following is taken from GNU coreutils' "expr --help" output.
    println!(
        r#"Usage: expr EXPRESSION
  or:  expr OPTION

      --help       display this help and exit
      --version    output version information and exit

Print the value of EXPRESSION to standard output.  A blank line below
separates increasing precedence groups.  EXPRESSION may be:

  ARG1 | ARG2       ARG1 if it is neither null nor 0, otherwise ARG2

  ARG1 & ARG2       ARG1 if neither argument is null or 0, otherwise 0

  ARG1 < ARG2       ARG1 is less than ARG2
  ARG1 <= ARG2      ARG1 is less than or equal to ARG2
  ARG1 = ARG2       ARG1 is equal to ARG2
  ARG1 != ARG2      ARG1 is unequal to ARG2
  ARG1 >= ARG2      ARG1 is greater than or equal to ARG2
  ARG1 > ARG2       ARG1 is greater than ARG2

  ARG1 + ARG2       arithmetic sum of ARG1 and ARG2
  ARG1 - ARG2       arithmetic difference of ARG1 and ARG2

  ARG1 * ARG2       arithmetic product of ARG1 and ARG2
  ARG1 / ARG2       arithmetic quotient of ARG1 divided by ARG2
  ARG1 % ARG2       arithmetic remainder of ARG1 divided by ARG2

  STRING : REGEXP   anchored pattern match of REGEXP in STRING

  match STRING REGEXP        same as STRING : REGEXP
  substr STRING POS LENGTH   substring of STRING, POS counted from 1
  index STRING CHARS         index in STRING where any CHARS is found, or 0
  length STRING              length of STRING
  + TOKEN                    interpret TOKEN as a string, even if it is a
                               keyword like 'match' or an operator like '/'

  ( EXPRESSION )             value of EXPRESSION

Beware that many operators need to be escaped or quoted for shells.
Comparisons are arithmetic if both ARGs are numbers, else lexicographical.
Pattern matches return the string matched between \( and \) or null; if
\( and \) are not used, they return the number of characters matched or 0.

Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null
or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.

Environment variables:
    * EXPR_DEBUG_TOKENS=1   dump expression's tokens
    * EXPR_DEBUG_RPN=1      dump expression represented in reverse polish notation
    * EXPR_DEBUG_SYA_STEP=1 dump each parser step
    * EXPR_DEBUG_AST=1      dump expression represented abstract syntax tree"#
    );
}

fn print_version() {
    println!("{} {}", uucore::util_name(), crate_version!());
}
<commit_msg>Fixes missing help on `expr` user docs.<commit_after>//* This file is part of the uutils coreutils package.
//*
//* (c) Roman Gafiyatullin <r.gafiyatullin@me.com>
//*
//* For the full copyright and license information, please view the LICENSE
//* file that was distributed with this source code.

use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{UResult, USimpleError};
use uucore::InvalidEncodingHandling;

mod syntax_tree;
mod tokens;

const VERSION: &str = "version";
const HELP: &str = "help";
static ABOUT: &str = "Print the value of EXPRESSION to standard output";
static USAGE: &str = r#"
    expr [EXPRESSION]
    expr [OPTIONS]"#;

pub fn uu_app<'a>() -> App<'a> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .override_usage(USAGE)
        .setting(AppSettings::InferLongArgs)
        .arg(
            Arg::new(VERSION)
                .long(VERSION)
                .help("output version information and exit"),
        )
        .arg(Arg::new(HELP).long(HELP).help("display this help and exit"))
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    // For expr utility we do not want getopts.
    // The following usage should work without escaping hyphens: `expr -15 = 1 +  2 \* \( 3 - -4 \)`

    if maybe_handle_help_or_version(&args) {
        Ok(())
    } else {
        let token_strings = args[1..].to_vec();

        match process_expr(&token_strings) {
            Ok(expr_result) => print_expr_ok(&expr_result),
            Err(expr_error) => Err(USimpleError::new(2, &expr_error)),
        }
    }
}

fn process_expr(token_strings: &[String]) -> Result<String, String> {
    let maybe_tokens = tokens::strings_to_tokens(token_strings);
    let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens);
    evaluate_ast(maybe_ast)
}

fn print_expr_ok(expr_result: &str) -> UResult<()> {
    println!("{}", expr_result);
    if expr_result == "0" || expr_result.is_empty() {
        Err(1.into())
    } else {
        Ok(())
    }
}

fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
    maybe_ast.and_then(|ast| ast.evaluate())
}

fn maybe_handle_help_or_version(args: &[String]) -> bool {
    if args.len() == 2 {
        if args[1] == "--help" {
            print_help();
            true
        } else if args[1] == "--version" {
            print_version();
            true
        } else {
            false
        }
    } else {
        false
    }
}

fn print_help() {
    //! The following is taken from GNU coreutils' "expr --help" output.
    println!(
        r#"Usage: expr EXPRESSION
  or:  expr OPTION

      --help       display this help and exit
      --version    output version information and exit

Print the value of EXPRESSION to standard output.  A blank line below
separates increasing precedence groups.  EXPRESSION may be:

  ARG1 | ARG2       ARG1 if it is neither null nor 0, otherwise ARG2

  ARG1 & ARG2       ARG1 if neither argument is null or 0, otherwise 0

  ARG1 < ARG2       ARG1 is less than ARG2
  ARG1 <= ARG2      ARG1 is less than or equal to ARG2
  ARG1 = ARG2       ARG1 is equal to ARG2
  ARG1 != ARG2      ARG1 is unequal to ARG2
  ARG1 >= ARG2      ARG1 is greater than or equal to ARG2
  ARG1 > ARG2       ARG1 is greater than ARG2

  ARG1 + ARG2       arithmetic sum of ARG1 and ARG2
  ARG1 - ARG2       arithmetic difference of ARG1 and ARG2

  ARG1 * ARG2       arithmetic product of ARG1 and ARG2
  ARG1 / ARG2       arithmetic quotient of ARG1 divided by ARG2
  ARG1 % ARG2       arithmetic remainder of ARG1 divided by ARG2

  STRING : REGEXP   anchored pattern match of REGEXP in STRING

  match STRING REGEXP        same as STRING : REGEXP
  substr STRING POS LENGTH   substring of STRING, POS counted from 1
  index STRING CHARS         index in STRING where any CHARS is found, or 0
  length STRING              length of STRING
  + TOKEN                    interpret TOKEN as a string, even if it is a
                               keyword like 'match' or an operator like '/'

  ( EXPRESSION )             value of EXPRESSION

Beware that many operators need to be escaped or quoted for shells.
Comparisons are arithmetic if both ARGs are numbers, else lexicographical.
Pattern matches return the string matched between \( and \) or null; if
\( and \) are not used, they return the number of characters matched or 0.

Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null
or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.

Environment variables:
    * EXPR_DEBUG_TOKENS=1   dump expression's tokens
    * EXPR_DEBUG_RPN=1      dump expression represented in reverse polish notation
    * EXPR_DEBUG_SYA_STEP=1 dump each parser step
    * EXPR_DEBUG_AST=1      dump expression represented abstract syntax tree"#
    );
}

fn print_version() {
    println!("{} {}", uucore::util_name(), crate_version!());
}
<|endoftext|>