<commit_before>#![allow(dead_code)]
use std::collections::hash_map::HashMap;
use std::io::BufReader;
use std::io::prelude::*;
use std::process::ChildStdin;
use std::process::Command;
use std::process::Stdio;
use std::sync::mpsc;
use std::thread;

use serde_json;
use serde_json::builder::*;
use serde_json::Value;

use view::View;

pub struct Core {
    stdin: ChildStdin,
    pub update_rx: mpsc::Receiver<Value>,
    rpc_rx: mpsc::Receiver<(u64,Value)>, // ! A simple piping works only for synchronous calls.
    rpc_index: u64,
    current_view: String,
    views: HashMap<String, View>,
}

impl Core {
    pub fn new(executable: &str, file: &str) -> Core {
        // spawn the core process
        let process = Command::new(executable)
                                .arg(file)
                                .stdout(Stdio::piped())
                                .stdin(Stdio::piped())
                                .stderr(Stdio::piped())
                                .env("RUST_BACKTRACE", "1")
                                .spawn()
                                .unwrap_or_else(|e| { panic!("failed to execute core: {}", e) });

        let (update_tx, update_rx) = mpsc::channel();
        let (rpc_tx, rpc_rx) = mpsc::channel();
        let stdout = process.stdout.unwrap();
        thread::spawn(move || {
            for line in BufReader::new(stdout).lines() {
                if let Ok(data) = serde_json::from_slice::<Value>(line.unwrap().as_bytes()) {
                    let req = data.as_object().unwrap();
                    info!("<<< {:?}", req);
                    if let (Some(id), Some(result)) = (req.get("id"), req.get("result")) {
                        rpc_tx.send((id.as_u64().unwrap(), result.clone())).unwrap();
                        info!(">>> {:?}", (id.as_u64().unwrap(), result.clone()));
                    } else if let (Some(method), Some(params)) = (req.get("method"), req.get("params")) {
                        let meth = method.as_str().unwrap();
                        if meth == "set_style" || meth == "scroll_to" || meth == "update" {
                            let request = ArrayBuilder::new().push(method.clone()).push(params.clone()).build();
                            update_tx.send(request).unwrap();
                        } else {
                            panic!("Unknown method {:?}.", method.as_str().unwrap());
                        }
                    } else {
                        panic!("Could not parse the core output: {:?}", req);
                    }
                }
            }
        });

        let stderr = process.stderr.unwrap();
        thread::spawn(move || {
            let buf_reader = BufReader::new(stderr);
            for line in buf_reader.lines() {
                if let Ok(line) = line {
                    error!("[core] {}", line);
                }
            }
        });

        let stdin = process.stdin.unwrap();

        let mut core = Core {
            stdin: stdin,
            update_rx: update_rx,
            rpc_rx: rpc_rx,
            rpc_index: 0,
            current_view: "".into(),
            views: HashMap::new(),
        };
        let view_id = core.new_view(Some(file.to_string())).as_str().unwrap().to_string();
        let view = View::new(file.to_string());
        core.views.insert(view_id.clone(), view);
        core.current_view = view_id;
        core
    }

    /// Build and send a JSON RPC request, returning the associated request ID to pair it with
    /// the response
    fn request(&mut self, method: &str, params: Value) -> u64 {
        self.rpc_index += 1;
        let message = ObjectBuilder::new()
            .insert("id", self.rpc_index)
            .insert("method", method)
            .insert("params", params)
            .build();
        self.send(&message);
        self.rpc_index
    }

    /// Build and send a JSON RPC notification. No synchronous response is expected, so
    /// there is no ID.
    fn notify(&mut self, method: &str, params: Value) {
        let message = ObjectBuilder::new()
            .insert("method", method)
            .insert("params", params)
            .build();
        self.send(&message);
    }

    /// Serialize JSON object and send it to the server
    fn send(&mut self, message: &Value) {
        let mut str_msg = serde_json::ser::to_string(&message).unwrap();
        str_msg.push('\n');
        self.stdin.write(str_msg.as_bytes()).unwrap();
    }

    fn call_sync(&mut self, method: &str, params: Value) -> Value {
        let i = self.request(method, params);
        let (id,result) = self.rpc_rx.recv().unwrap();
        assert_eq!(i, id);
        result
    }

    fn call_edit(&mut self, method: &str, params: Option<Value>) {
        let obj = ObjectBuilder::new()
            .insert("method", method)
            .insert("view_id", &self.current_view)
            .insert("params", params.unwrap_or(ArrayBuilder::new().build()));
        let built_obj = obj.build();
        info!(">>> {:?}", built_obj);
        self.notify("edit", built_obj);
    }

    fn call_edit_sync(&mut self, method: &str, params: Option<Value>) -> Value{
        let obj = ObjectBuilder::new()
            .insert("method", method)
            .insert("view_id", &self.current_view)
            .insert("params", params.unwrap_or(ArrayBuilder::new().build()));
        self.call_sync("edit", obj.build())
    }

    pub fn new_view(&mut self, file_path: Option<String>) -> Value {
        let mut obj = ObjectBuilder::new();
        if file_path.is_some() {
            obj = obj.insert("file_path", file_path.unwrap());
        }
        self.call_sync("new_view", obj.build())
    }

    pub fn save(&mut self) {
        let views = self.views.clone();
        let save_params = ObjectBuilder::new()
            .insert("view_id", &self.current_view.clone())
            .insert("file_path", views.get(&self.current_view).unwrap().filepath.clone())
            .build();
        self.request("save", save_params);
    }

    pub fn open(&mut self, filename: &str) {
        self.call_edit("open", Some(ObjectBuilder::new().insert("filename", filename).build()));
    }

    pub fn left(&mut self) { self.call_edit("move_left", None); }
    pub fn left_sel(&mut self) { self.call_edit("move_left_and_modify_selection", None); }

    pub fn right(&mut self) { self.call_edit("move_right", None); }
    pub fn right_sel(&mut self) { self.call_edit("move_right_and_modify_selection", None); }

    pub fn up(&mut self) { self.call_edit("move_up", None); }
    pub fn up_sel(&mut self) { self.call_edit("move_up_and_modify_selection", None); }

    pub fn down(&mut self) { self.call_edit("move_down", None); }
    pub fn down_sel(&mut self) { self.call_edit("move_down_and_modify_selection", None); }

    pub fn del(&mut self) { self.call_edit("delete_backward", None); }

    pub fn page_up(&mut self) { self.call_edit("page_up", None); }
    pub fn page_up_sel(&mut self) { self.call_edit("page_up_and_modify_selection", None); }

    pub fn page_down(&mut self) { self.call_edit("page_down", None); }
    pub fn page_down_sel(&mut self) { self.call_edit("page_down_and_modify_selection", None); }

    pub fn insert_newline(&mut self) { self.call_edit("insert_newline", None); }

    pub fn f1(&mut self) { self.call_edit("debug_rewrap", None); }

    pub fn f2(&mut self) { self.call_edit("debug_test_fg_spans", None); }

    pub fn char(&mut self, ch: char) {
        self.call_edit("insert", Some(ObjectBuilder::new().insert("chars", ch).build()));
    }

    pub fn scroll(&mut self, start: u64, end: u64) {
        self.call_edit("scroll", Some(ArrayBuilder::new().push(start).push(end).build()));
    }

    pub fn click(&mut self, line: u64, column: u64) {
        self.call_edit("click", Some(ArrayBuilder::new().push(line).push(column).push(0).push(1).build()));
    }
    pub fn drag(&mut self, line: u64, column: u64) {
        self.call_edit("drag", Some(ArrayBuilder::new().push(line).push(column).push(0).push(1).build()));
    }

    pub fn copy(&mut self) -> String {
        self.call_edit_sync("copy", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn cut(&mut self) -> String {
        self.call_edit_sync("cut", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn paste(&mut self, s: String) {
        self.call_edit("insert", Some(ObjectBuilder::new().insert("chars", s).build()));
    }

    #[allow(dead_code)]
    pub fn test(&mut self) {
        self.render_lines(0, 10);
    }

    pub fn render_lines(&mut self, _start: u64, _end: u64) {
        unimplemented!()
        // self.rpc_index += 1;
        // println!("render_lines");
        // let value = ArrayBuilder::new()
        //     .push("rpc")
        //     .push_object(|builder| builder
        //         .insert("index", self.rpc_index)
        //         .insert_array("request", |builder| builder
        //             .push("render_lines")
        //             .push_object(|builder| builder
        //                 .insert("first_line", _start)
        //                 .insert("last_line", _end)
        //             )
        //         )
        //     ).unwrap();
        // self.write(value);
    }

    pub fn render_lines_sync(&mut self, _start: u64, _end: u64) -> Value {
        unimplemented!()
        // self.render_lines(_start, _end);
        // let value = self.rpc_rx.recv().unwrap();
        // let object = value.as_object().unwrap();
        // assert_eq!(self.rpc_index, object.get("index").unwrap().as_u64().unwrap());
        // object.get("result").unwrap().clone()
    }
}
<commit_msg>Adds an update instruction to the Core implementation<commit_after>#![allow(dead_code)]
use std::collections::hash_map::HashMap;
use std::io::BufReader;
use std::io::prelude::*;
use std::process::ChildStdin;
use std::process::Command;
use std::process::Stdio;
use std::sync::mpsc;
use std::thread;

use serde_json;
use serde_json::builder::*;
use serde_json::Value;

use update::Update;
use view::View;

pub struct Core {
    stdin: ChildStdin,
    pub update_rx: mpsc::Receiver<Value>,
    rpc_rx: mpsc::Receiver<(u64,Value)>, // ! A simple piping works only for synchronous calls.
    rpc_index: u64,
    current_view: String,
    views: HashMap<String, View>,
}

impl Core {
    pub fn new(executable: &str, file: &str) -> Core {
        // spawn the core process
        let process = Command::new(executable)
                                .arg(file)
                                .stdout(Stdio::piped())
                                .stdin(Stdio::piped())
                                .stderr(Stdio::piped())
                                .env("RUST_BACKTRACE", "1")
                                .spawn()
                                .unwrap_or_else(|e| { panic!("failed to execute core: {}", e) });

        let (update_tx, update_rx) = mpsc::channel();
        let (rpc_tx, rpc_rx) = mpsc::channel();
        let stdout = process.stdout.unwrap();
        thread::spawn(move || {
            for line in BufReader::new(stdout).lines() {
                if let Ok(data) = serde_json::from_slice::<Value>(line.unwrap().as_bytes()) {
                    let req = data.as_object().unwrap();
                    info!("<<< {:?}", req);
                    if let (Some(id), Some(result)) = (req.get("id"), req.get("result")) {
                        rpc_tx.send((id.as_u64().unwrap(), result.clone())).unwrap();
                        info!(">>> {:?}", (id.as_u64().unwrap(), result.clone()));
                    } else if let (Some(method), Some(params)) = (req.get("method"), req.get("params")) {
                        let meth = method.as_str().unwrap();
                        if meth == "set_style" || meth == "scroll_to" || meth == "update" {
                            let request = ArrayBuilder::new().push(method.clone()).push(params.clone()).build();
                            update_tx.send(request).unwrap();
                        } else {
                            panic!("Unknown method {:?}.", method.as_str().unwrap());
                        }
                    } else {
                        panic!("Could not parse the core output: {:?}", req);
                    }
                }
            }
        });

        let stderr = process.stderr.unwrap();
        thread::spawn(move || {
            let buf_reader = BufReader::new(stderr);
            for line in buf_reader.lines() {
                if let Ok(line) = line {
                    error!("[core] {}", line);
                }
            }
        });

        let stdin = process.stdin.unwrap();

        let mut core = Core {
            stdin: stdin,
            update_rx: update_rx,
            rpc_rx: rpc_rx,
            rpc_index: 0,
            current_view: "".into(),
            views: HashMap::new(),
        };
        let view_id = core.new_view(Some(file.to_string())).as_str().unwrap().to_string();
        let view = View::new(file.to_string());
        core.views.insert(view_id.clone(), view);
        core.current_view = view_id;
        core
    }

    pub fn update(&mut self, update: &Update) {
        let mut view = self.view();
        view.update(update);
        self.views.insert(self.current_view.clone(), view);
    }

    pub fn view(&self) -> View {
        self.views.clone().get(&self.current_view).unwrap().clone()
    }

    /// Build and send a JSON RPC request, returning the associated request ID to pair it with
    /// the response
    fn request(&mut self, method: &str, params: Value) -> u64 {
        self.rpc_index += 1;
        let message = ObjectBuilder::new()
            .insert("id", self.rpc_index)
            .insert("method", method)
            .insert("params", params)
            .build();
        self.send(&message);
        self.rpc_index
    }

    /// Build and send a JSON RPC notification. No synchronous response is expected, so
    /// there is no ID.
    fn notify(&mut self, method: &str, params: Value) {
        let message = ObjectBuilder::new()
            .insert("method", method)
            .insert("params", params)
            .build();
        self.send(&message);
    }

    /// Serialize JSON object and send it to the server
    fn send(&mut self, message: &Value) {
        let mut str_msg = serde_json::ser::to_string(&message).unwrap();
        str_msg.push('\n');
        self.stdin.write(str_msg.as_bytes()).unwrap();
    }

    fn call_sync(&mut self, method: &str, params: Value) -> Value {
        let i = self.request(method, params);
        let (id,result) = self.rpc_rx.recv().unwrap();
        assert_eq!(i, id);
        result
    }

    fn call_edit(&mut self, method: &str, params: Option<Value>) {
        let obj = ObjectBuilder::new()
            .insert("method", method)
            .insert("view_id", &self.current_view)
            .insert("params", params.unwrap_or(ArrayBuilder::new().build()));
        let built_obj = obj.build();
        info!(">>> {:?}", built_obj);
        self.notify("edit", built_obj);
    }

    fn call_edit_sync(&mut self, method: &str, params: Option<Value>) -> Value{
        let obj = ObjectBuilder::new()
            .insert("method", method)
            .insert("view_id", &self.current_view)
            .insert("params", params.unwrap_or(ArrayBuilder::new().build()));
        self.call_sync("edit", obj.build())
    }

    pub fn new_view(&mut self, file_path: Option<String>) -> Value {
        let mut obj = ObjectBuilder::new();
        if file_path.is_some() {
            obj = obj.insert("file_path", file_path.unwrap());
        }
        self.call_sync("new_view", obj.build())
    }

    pub fn save(&mut self) {
        let views = self.views.clone();
        let save_params = ObjectBuilder::new()
            .insert("view_id", &self.current_view.clone())
            .insert("file_path", views.get(&self.current_view).unwrap().filepath.clone())
            .build();
        self.request("save", save_params);
    }

    pub fn open(&mut self, filename: &str) {
        self.call_edit("open", Some(ObjectBuilder::new().insert("filename", filename).build()));
    }

    pub fn left(&mut self) { self.call_edit("move_left", None); }
    pub fn left_sel(&mut self) { self.call_edit("move_left_and_modify_selection", None); }

    pub fn right(&mut self) { self.call_edit("move_right", None); }
    pub fn right_sel(&mut self) { self.call_edit("move_right_and_modify_selection", None); }

    pub fn up(&mut self) { self.call_edit("move_up", None); }
    pub fn up_sel(&mut self) { self.call_edit("move_up_and_modify_selection", None); }

    pub fn down(&mut self) { self.call_edit("move_down", None); }
    pub fn down_sel(&mut self) { self.call_edit("move_down_and_modify_selection", None); }

    pub fn del(&mut self) { self.call_edit("delete_backward", None); }

    pub fn page_up(&mut self) { self.call_edit("page_up", None); }
    pub fn page_up_sel(&mut self) { self.call_edit("page_up_and_modify_selection", None); }

    pub fn page_down(&mut self) { self.call_edit("page_down", None); }
    pub fn page_down_sel(&mut self) { self.call_edit("page_down_and_modify_selection", None); }

    pub fn insert_newline(&mut self) { self.call_edit("insert_newline", None); }

    pub fn f1(&mut self) { self.call_edit("debug_rewrap", None); }

    pub fn f2(&mut self) { self.call_edit("debug_test_fg_spans", None); }

    pub fn char(&mut self, ch: char) {
        self.call_edit("insert", Some(ObjectBuilder::new().insert("chars", ch).build()));
    }

    pub fn scroll(&mut self, start: u64, end: u64) {
        self.call_edit("scroll", Some(ArrayBuilder::new().push(start).push(end).build()));
    }

    pub fn click(&mut self, line: u64, column: u64) {
        self.call_edit("click", Some(ArrayBuilder::new().push(line).push(column).push(0).push(1).build()));
    }
    pub fn drag(&mut self, line: u64, column: u64) {
        self.call_edit("drag", Some(ArrayBuilder::new().push(line).push(column).push(0).push(1).build()));
    }

    pub fn copy(&mut self) -> String {
        self.call_edit_sync("copy", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn cut(&mut self) -> String {
        self.call_edit_sync("cut", None).as_str().map(|x|x.into()).unwrap()
    }
    pub fn paste(&mut self, s: String) {
        self.call_edit("insert", Some(ObjectBuilder::new().insert("chars", s).build()));
    }

    #[allow(dead_code)]
    pub fn test(&mut self) {
        self.render_lines(0, 10);
    }

    pub fn render_lines(&mut self, _start: u64, _end: u64) {
        unimplemented!()
        // self.rpc_index += 1;
        // println!("render_lines");
        // let value = ArrayBuilder::new()
        //     .push("rpc")
        //     .push_object(|builder| builder
        //         .insert("index", self.rpc_index)
        //         .insert_array("request", |builder| builder
        //             .push("render_lines")
        //             .push_object(|builder| builder
        //                 .insert("first_line", _start)
        //                 .insert("last_line", _end)
        //             )
        //         )
        //     ).unwrap();
        // self.write(value);
    }

    pub fn render_lines_sync(&mut self, _start: u64, _end: u64) -> Value {
        unimplemented!()
        // self.render_lines(_start, _end);
        // let value = self.rpc_rx.recv().unwrap();
        // let object = value.as_object().unwrap();
        // assert_eq!(self.rpc_index, object.get("index").unwrap().as_u64().unwrap());
        // object.get("result").unwrap().clone()
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use self::chrono::offset::Local;
use std::fs::{File, OpenOptions};
use std::io::{self, Write};

pub struct Logger {
    logfile: Option<File>,
}

impl Logger {
    pub fn new() -> Logger {
        Logger {
            logfile: None,
        }
    }

    pub fn set_logfile(&mut self, filename: &str) {
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(filename);

        match file {
            Ok(file) => self.logfile = Some(file),
            Err(error) => eprintln!("Couldn't open `{}' as a logfile: {}", filename, error),
        }
    }

    pub fn log(&mut self, message: &str) -> io::Result<()> {
        if let Some(ref mut logfile) = self.logfile {
            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
            let line = format!("[{}] {}\n", timestamp, message);

            logfile.write_all(line.as_bytes())?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    extern crate tempfile;

    use super::*;

    use self::tempfile::TempDir;
    use self::chrono::{TimeZone, Duration};
    use std::io::{self, BufReader, BufRead};
    use std::path;

    fn setup_logger() -> io::Result<(TempDir, path::PathBuf, Logger)> {
        let tmp = TempDir::new()?;
        let logfile = {
            let mut logfile = tmp.path().to_owned();
            logfile.push("example.log");
            logfile
        };
        assert!(!logfile.exists());

        let mut logger = Logger::new();
        logger.set_logfile(logfile.to_str().unwrap());

        Ok((tmp, logfile, logger))
    }

    #[test]
    fn t_set_logfile() -> io::Result<()> {
        let (_tmp, logfile, _logger) = setup_logger()?;

        assert!(logfile.exists());

        Ok(())
    }

    #[test]
    fn t_log_writes_message_to_the_file() -> io::Result<()> {
        let (_tmp, logfile, mut logger) = setup_logger()?;

        let messages = {
            let mut messages = vec![];
            messages.push("Hello, world!");
            messages.push("I'm doing fine, how are you?");
            messages.push("Time to wrap up, see ya!");
            messages
        };

        let start_time = Local::now();
        for msg in &messages {
            logger.log(msg)?;
        }
        let finish_time = Local::now();

        // Dropping logger to force it to flush the log and close the file
        drop(logger);

        let file = File::open(logfile)?;
        let reader = BufReader::new(file);
        for (line, expected) in reader.lines().zip(messages) {
            match line {
                Ok(line) => {
                    assert!(line.starts_with("["));

                    let timestamp_end = line.find(']').expect("Failed to find the end of the timestamp");
                    // "]" is an ASCII character, so it occupies one byte in UTF-8. As a result, we
                    // can simply add one to get the next character's byte offset. And since the
                    // next char should be a space, which is ASCII as well, we can just add 2.
                    //
                    // If timestamp is followed by something non-ASCII, indexing into &str will
                    // panic and fail the test, which is good.
                    assert_eq!(&line[timestamp_end+1..timestamp_end+2], " ");

                    // Timestamp starts with "[", we skip it by starting at 1 rather than 0.
                    let timestamp_str = &line[1..timestamp_end];
                    let timestamp =
                        Local::now()
                        .timezone()
                        .datetime_from_str(timestamp_str, "%Y-%m-%d %H:%M:%S")
                        .expect("Failed to parse the timestamp from the log file");
                    // `start_time` and `end_time` may have millisecond precision or better,
                    // whereas `timestamp` is limited to seconds. Therefore, we account for
                    // a situation where `start_time` is slightly bigger than `timestamp`.
                    assert!(timestamp - start_time > Duration::seconds(-1));
                    assert!(finish_time >= timestamp);

                    // Message starts after "] " that follows the timestamp, hence +2.
                    let message_str = &line[timestamp_end+2..];
                    assert_eq!(message_str, expected);
                }
                Err(e) => return Err(e),
            }
        }

        Ok(())
    }
}
<commit_msg>Add log levels to Rust logger<commit_after>extern crate chrono;

use self::chrono::offset::Local;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io::{self, Write};

pub enum Level {
    None,
    UserError,
    Critical,
    Error,
    Warn,
    Info,
    Debug,
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Level::None => write!(f, "NONE"),
            Level::UserError => write!(f, "USERERROR"),
            Level::Critical => write!(f, "CRITICAL"),
            Level::Error => write!(f, "ERROR"),
            Level::Warn => write!(f, "WARNING"),
            Level::Info => write!(f, "INFO"),
            Level::Debug => write!(f, "DEBUG"),
        }
    }
}

pub struct Logger {
    logfile: Option<File>,
}

impl Logger {
    pub fn new() -> Logger {
        Logger {
            logfile: None,
        }
    }

    pub fn set_logfile(&mut self, filename: &str) {
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(filename);

        match file {
            Ok(file) => self.logfile = Some(file),
            Err(error) => eprintln!("Couldn't open `{}' as a logfile: {}", filename, error),
        }
    }

    pub fn log(&mut self, level: Level, message: &str) -> io::Result<()> {
        if let Some(ref mut logfile) = self.logfile {
            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
            let line = format!("[{}] {}: {}\n", timestamp, level, message);

            logfile.write_all(line.as_bytes())?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    extern crate tempfile;

    use super::*;

    use self::tempfile::TempDir;
    use self::chrono::{TimeZone, Duration};
    use std::io::{self, BufReader, BufRead};
    use std::path;

    fn setup_logger() -> io::Result<(TempDir, path::PathBuf, Logger)> {
        let tmp = TempDir::new()?;
        let logfile = {
            let mut logfile = tmp.path().to_owned();
            logfile.push("example.log");
            logfile
        };
        assert!(!logfile.exists());

        let mut logger = Logger::new();
        logger.set_logfile(logfile.to_str().unwrap());

        Ok((tmp, logfile, logger))
    }

    #[test]
    fn t_set_logfile() -> io::Result<()> {
        let (_tmp, logfile, _logger) = setup_logger()?;

        assert!(logfile.exists());

        Ok(())
    }

    #[test]
    fn t_log_writes_message_to_the_file() -> io::Result<()> {
        let (_tmp, logfile, mut logger) = setup_logger()?;

        let messages = {
            let mut messages = vec![];
            messages.push("Hello, world!");
            messages.push("I'm doing fine, how are you?");
            messages.push("Time to wrap up, see ya!");
            messages
        };

        let start_time = Local::now();
        for msg in &messages {
            logger.log(Level::Debug, msg)?;
        }
        let finish_time = Local::now();

        // Dropping logger to force it to flush the log and close the file
        drop(logger);

        let file = File::open(logfile)?;
        let reader = BufReader::new(file);
        for (line, expected) in reader.lines().zip(messages) {
            match line {
                Ok(line) => {
                    assert!(line.starts_with("["));

                    let timestamp_end = line.find(']').expect("Failed to find the end of the timestamp");
                    // "]" is an ASCII character, so it occupies one byte in UTF-8. As a result, we
                    // can simply add one to get the next character's byte offset. And since the
                    // next char should be a space, which is ASCII as well, we can just add 2.
                    //
                    // If timestamp is followed by something non-ASCII, indexing into &str will
                    // panic and fail the test, which is good.
                    assert_eq!(&line[timestamp_end+1..timestamp_end+2], " ");

                    // Timestamp starts with "[", we skip it by starting at 1 rather than 0.
                    let timestamp_str = &line[1..timestamp_end];
                    let timestamp =
                        Local::now()
                        .timezone()
                        .datetime_from_str(timestamp_str, "%Y-%m-%d %H:%M:%S")
                        .expect("Failed to parse the timestamp from the log file");
                    // `start_time` and `end_time` may have millisecond precision or better,
                    // whereas `timestamp` is limited to seconds. Therefore, we account for
                    // a situation where `start_time` is slightly bigger than `timestamp`.
                    assert!(timestamp - start_time > Duration::seconds(-1));
                    assert!(finish_time >= timestamp);

                    // Message starts after "] " that follows the timestamp, hence +2.
                    let message_str = &line[timestamp_end+2..];
                    let level = String::from("DEBUG: ");
                    assert_eq!(message_str, level + expected);
                }
                Err(e) => return Err(e),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate coreutils;

use std::env::args;
use std::io::{self, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;

use coreutils::extra::OptionalExt;

static LONG_HELP: &'static str = r#"
    NAME
        rem - set a count-down.
    SYNOPSIS
        rem [-h | --help] [-m N | --minutes N] [-H N | --hours N] [-s N | --seconds N] [-M N | --milliseconds N] [-n | --len] [-b | --blink]
    DESCRIPTION
        This utility lets you set a count-down with a progress bar. The options can be given in combination, adding together the durations given.
    OPTIONS
        --help
            Print this manual page.
        -h
            Print short help page.
        -m N
        --minutes N
            Wait N minutes.
        -H N
        --hours N
            Wait N hours.
        -s N
        --seconds N
            Wait N seconds.
        -M N
        --milliseconds N
            Wait N milliseconds.
        -n N
        --len N
            Set the length of the progress bar to N.
        -b
        --blink
            Blink with a red banner when done.
    AUTHOR
        This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be reported in the Github repository, 'redox-os/extrautils'.
    COPYRIGHT
        Copyright (c) 2016 Ticki

        Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#;

static SHORT_HELP: &'static str = r#"
    rem - set a count-down.

    Options (use --help for extended list):
    -m N          => Wait N minutes.
    -H N          => Wait N hours.
    -s N          => Wait N seconds.
    -M N          => Wait N milliseconds.
    -n N          => N character progress bar.
    -b            => Blink when done.
"#;

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let mut ms = 0u64;
    let mut len = 20;
    let mut blink = false;

    // Loop over the arguments.
    loop {
        let arg = if let Some(x) = args.next() {
            x
        } else {
            break;
        };

        match arg.as_str() {
            "--help" => {
                // Print help.
                stdout.write(LONG_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            "-h" => {
                // Print help.
                stdout.write(SHORT_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            "-n" | "--len" => len = args.next().fail("no number after -n.", &mut stderr).parse().try(&mut stderr),
            "-b" | "--blink" => blink = true,
            t => {
                // Find number input.
                let num: u64 = args.next().unwrap_or_else(|| {
                    stderr.write(b"error: incorrectly formatted number.\
                                   Please input a positive integer.").try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    exit(1);
                }).parse().try(&mut stderr);
                ms += num * match t {
                    "-m" | "--minutes" => 1000 * 60,
                    "-H" | "--hours" => 1000 * 60 * 60,
                    "-s" | "--seconds" => 1000,
                    "-M" | "--milliseconds" => 1,
                    _ => {
                        // Unknown argument.
                        stderr.write(b"error: unknown argument, ").try(&mut stderr);
                        stderr.write(t.as_bytes()).try(&mut stderr);
                        stderr.write(b".\n").try(&mut stderr);
                        stderr.flush().try(&mut stderr);
                        exit(1);
                    },

                };
            },
        }
    }

    // Hide the cursor.
    stdout.write(b"\x1b[?25l").try(&mut stderr);
    // Draw the empty progress bar.
    for _ in 0..len + 1 {
        stdout.write(b" ").try(&mut stderr);
    }
    stdout.write(b"]").try(&mut stderr);

    stdout.write(b"\r[").try(&mut stderr);

    // As time goes, update the progress bar.
    for _ in 0..len {
        stdout.write(b"#").try(&mut stderr);
        stdout.flush().try(&mut stderr);
        // Sleep.
        sleep(Duration::from_millis(ms / len));
    }

    if blink {
        // This will print a blinking red banner.
        for _ in 0..13 {
            // Set drawing mode to red background.
            stdout.write(b"\x1b[41m").try(&mut stderr);
            // Clear the current line, rendering the background red.
            stdout.write(b"\x1b[2K").try(&mut stderr);
            // Flush.
            stdout.flush().try(&mut stderr);
            // Sleep.
            sleep(Duration::from_millis(200));

            // Clear the drawing mode.
            stdout.write(b"\x1b[0m").try(&mut stderr);
            // Clear the background.
            stdout.write(b"\x1b[2K").try(&mut stderr);
            // Flush.
            stdout.flush().try(&mut stderr);
            sleep(Duration::from_millis(200));

            // Repeat...
        }
    }

    // Show the cursor again.
    stdout.write(b"\x1b[?25h").try(&mut stderr);
    stdout.write(b"\n").try(&mut stderr);
}
<commit_msg>Newline after error<commit_after>#![deny(warnings)]

extern crate coreutils;

use std::env::args;
use std::io::{self, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;

use coreutils::extra::OptionalExt;

static LONG_HELP: &'static str = r#"
    NAME
        rem - set a count-down.
    SYNOPSIS
        rem [-h | --help] [-m N | --minutes N] [-H N | --hours N] [-s N | --seconds N] [-M N | --milliseconds N] [-n | --len] [-b | --blink]
    DESCRIPTION
        This utility lets you set a count-down with a progress bar. The options can be given in combination, adding together the durations given.
    OPTIONS
        --help
            Print this manual page.
        -h
            Print short help page.
        -m N
        --minutes N
            Wait N minutes.
        -H N
        --hours N
            Wait N hours.
        -s N
        --seconds N
            Wait N seconds.
        -M N
        --milliseconds N
            Wait N milliseconds.
        -n N
        --len N
            Set the length of the progress bar to N.
        -b
        --blink
            Blink with a red banner when done.
    AUTHOR
        This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be reported in the Github repository, 'redox-os/extrautils'.
    COPYRIGHT
        Copyright (c) 2016 Ticki

        Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#;

static SHORT_HELP: &'static str = r#"
    rem - set a count-down.

    Options (use --help for extended list):
    -m N          => Wait N minutes.
    -H N          => Wait N hours.
    -s N          => Wait N seconds.
    -M N          => Wait N milliseconds.
    -n N          => N character progress bar.
    -b            => Blink when done.
"#;

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let mut ms = 0u64;
    let mut len = 20;
    let mut blink = false;

    // Loop over the arguments.
    loop {
        let arg = if let Some(x) = args.next() {
            x
        } else {
            break;
        };

        match arg.as_str() {
            "--help" => {
                // Print help.
                stdout.write(LONG_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            "-h" => {
                // Print help.
                stdout.write(SHORT_HELP.as_bytes()).try(&mut stderr);
                return;
            },
            "-n" | "--len" => len = args.next().fail("no number after -n.", &mut stderr).parse().try(&mut stderr),
            "-b" | "--blink" => blink = true,
            t => {
                // Find number input.
                let num: u64 = args.next().unwrap_or_else(|| {
                    stderr.write(b"error: incorrectly formatted number. \
                                   Please input a positive integer.\n").try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    exit(1);
                }).parse().try(&mut stderr);
                ms += num * match t {
                    "-m" | "--minutes" => 1000 * 60,
                    "-H" | "--hours" => 1000 * 60 * 60,
                    "-s" | "--seconds" => 1000,
                    "-M" | "--milliseconds" => 1,
                    _ => {
                        // Unknown argument.
                        stderr.write(b"error: unknown argument, ").try(&mut stderr);
                        stderr.write(t.as_bytes()).try(&mut stderr);
                        stderr.write(b".\n").try(&mut stderr);
                        stderr.flush().try(&mut stderr);
                        exit(1);
                    },

                };
            },
        }
    }

    // Hide the cursor.
    stdout.write(b"\x1b[?25l").try(&mut stderr);
    // Draw the empty progress bar.
    for _ in 0..len + 1 {
        stdout.write(b" ").try(&mut stderr);
    }
    stdout.write(b"]").try(&mut stderr);

    stdout.write(b"\r[").try(&mut stderr);

    // As time goes, update the progress bar.
    for _ in 0..len {
        stdout.write(b"#").try(&mut stderr);
        stdout.flush().try(&mut stderr);
        // Sleep.
        sleep(Duration::from_millis(ms / len));
    }

    if blink {
        // This will print a blinking red banner.
        for _ in 0..13 {
            // Set drawing mode to red background.
            stdout.write(b"\x1b[41m").try(&mut stderr);
            // Clear the current line, rendering the background red.
            stdout.write(b"\x1b[2K").try(&mut stderr);
            // Flush.
            stdout.flush().try(&mut stderr);
            // Sleep.
            sleep(Duration::from_millis(200));

            // Clear the drawing mode.
            stdout.write(b"\x1b[0m").try(&mut stderr);
            // Clear the background.
            stdout.write(b"\x1b[2K").try(&mut stderr);
            // Flush.
            stdout.flush().try(&mut stderr);
            sleep(Duration::from_millis(200));

            // Repeat...
        }
    }

    // Show the cursor again.
    stdout.write(b"\x1b[?25h").try(&mut stderr);
    stdout.write(b"\n").try(&mut stderr);
}
<|endoftext|>


<commit_before>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::get_chat_action;
use bot::parse_mode::get_parse_mode;
use error::{Result, check_for_error};
use error::Error::{JsonNotFound, RequestFailed};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};

use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
               id: id,
               first_name: first_name,
               last_name: last_name,
               username: username,
               client: client,
               bot_url: bot_url,
           })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<f32>)
                       -> Result<Option<Vec<Update>>> {
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        let network_delay = network_delay.unwrap_or(0.0);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let params = [("offset", offset),
                     ("limit", limit),
                     ("timeout", timeout)];
        /*
        let url = format!("{}?offset={}&limit={}&timeout={}",
                          path_url,
                          offset,
                          limit,
                          timeout);*/
        let mut data = self.client.get(&path_url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str =
            &disable_web_page_preview.unwrap_or(&false).to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup =
            &reply_markup
                 .map(|r| serde_json::to_string(r).unwrap_or("".to_string()))
                 .unwrap_or("".to_string());

        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_message(&self,
                           update: &Update,
                           chat_id: &i32,
                           disable_notification: Option<&bool>)
                           -> Result<Message> {
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will show the given chat action to the users.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id), ("action", action)];
        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let chat_action_succeeded: bool = serde_json::from_value(result_json.clone())?;
        Ok(chat_action_succeeded)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup = &reply_markup
                                .and_then(|r| serde_json::to_string(r).ok())
                                .unwrap_or("".to_string());
        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<commit_msg>Using url parameter queries instead of url encoding because of inconsistent behaviour with Telegram's API<commit_after>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::get_chat_action;
use bot::parse_mode::get_parse_mode;
use error::{Result, check_for_error};
use error::Error::{JsonNotFound, RequestFailed};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};

use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
               id: id,
               first_name: first_name,
               last_name: last_name,
               username: username,
               client: client,
               bot_url: bot_url,
           })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<f32>)
                       -> Result<Option<Vec<Update>>> {
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        let network_delay = network_delay.unwrap_or(0.0);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let url = format!("{}?offset={}&limit={}&timeout={}&network_delay={}",
                          path_url,
                          offset,
                          limit,
                          timeout,
                          network_delay);
        println!("{}", url);
        let mut data = self.client.get(&url).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str =
            &disable_web_page_preview.unwrap_or(&false).to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup =
            &reply_markup
                 .map(|r| serde_json::to_string(r).unwrap_or("".to_string()))
                 .unwrap_or("".to_string());

        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_message(&self,
                           update: &Update,
                           chat_id: &i32,
                           disable_notification: Option<&bool>)
                           -> Result<Message> {
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will show the given chat action to the users.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id), ("action", action)];
        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let chat_action_succeeded: bool = serde_json::from_value(result_json.clone())?;
        Ok(chat_action_succeeded)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id
                                             .map(|i| i.to_string())
                                             .unwrap_or("None".to_string());
        let reply_markup = &reply_markup
                                .and_then(|r| serde_json::to_string(r).ok())
                                .unwrap_or("".to_string());
        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

macro_rules! parse_retval {
    ($retval:expr) => {{
        let retval = $retval;

        if retval == -1 {
            Err(std::io::Error::last_os_error())
        } else {
            Ok(retval)
        }
    }};
}

// Initialize an array with a type that doesn't implement Copy
macro_rules! init_array {
    ($val:expr, $len:expr) => {{
        let mut array: [_; $len] = unsafe { std::mem::uninitialized() };
        for elem in array.iter_mut() {
            unsafe {
                std::ptr::write(elem, $val);
            }
        }
        array
    }};
}
<commit_msg>Replace deprecated std::mem::uninitialized()<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

macro_rules! parse_retval {
    ($retval:expr) => {{
        let retval = $retval;

        if retval == -1 {
            Err(std::io::Error::last_os_error())
        } else {
            Ok(retval)
        }
    }};
}

// Initialize an array with a type that doesn't implement Copy
macro_rules! init_array {
    ($val:expr, $len:expr) => {{
        // Based on https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element

        use std::mem::{self, MaybeUninit};

        // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
        // safe because the type we are claiming to have initialized here is a
        // bunch of `MaybeUninit`s, which do not require initialization.
        let mut data: [MaybeUninit<_>; $len] = unsafe { MaybeUninit::uninit().assume_init() };

        // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
        // assignment instead of `ptr::write` does not cause the old
        // uninitialized value to be dropped. Also if there is a panic during
        // this loop, we have a memory leak, but there is no memory safety
        // issue.
        for elem in &mut data[..] {
            *elem = MaybeUninit::new($val);
        }

        // Everything is initialized. Transmute the array to the
        // initialized type.
        unsafe { mem::transmute::<_, [_; $len]>(data) }
    }};
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>create example code<commit_after>fn main() {
	let mut subject = "world";

	println!("Hello, {}!", subject);
	println!("Goodbye, {}!", subject);

	subject = "moon";

	println!("Hello, {}!", subject);
	println!("Goodbye, {}!", subject);
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! gc_ref_type {
    (pub struct $ref_type:ident / $storage_type:ident <'a> {
        $($field_name:ident / $field_setter_name:ident : $field_type: ty),*
    }) => {
        struct $storage_type <'a> {
            $($field_name: <$field_type as $crate::HeapInline<'a>>::Storage,)*
        }

        unsafe impl<'a> $crate::Mark<'a> for $storage_type<'a> {
            unsafe fn mark(ptr: *mut $storage_type<'a>) {
                if !$crate::Heap::get_mark_bit(ptr) {
                    $crate::Heap::set_mark_bit(ptr);
                    $(
                        $crate::Mark::mark(
                            &mut (*ptr).$field_name
                                as *mut <$field_type as $crate::HeapInline<'a>>::Storage);
                    )*
                }
            }
        }

        unsafe impl<'a> $crate::Mark<'a> for *mut $storage_type<'a> {
            unsafe fn mark(field_ptr: *mut *mut $storage_type<'a>) {
                let ptr = *field_ptr;
                if !ptr.is_null() {
                    Mark::mark(ptr);
                }
            }
        }

        #[allow(raw_pointer_derive)]
        #[derive(Clone, Debug, PartialEq)]
        pub struct $ref_type<'a>($crate::PinnedRef<'a, $storage_type<'a>>);

        impl<'a> $ref_type<'a> {
            $(
                pub fn $field_name(&self) -> $field_type {
                    let ptr = self.0.ptr;
                    unsafe {
                        <$field_type as $crate::HeapInline<'a>>::from_heap(
                            &*self.0.heap,
                            &(*ptr).$field_name)
                    }
                }

                pub fn $field_setter_name(&self, v: $field_type) {
                    let ptr = self.0.ptr;
                    unsafe {
                        (*ptr).$field_name =
                            <$field_type as $crate::HeapInline<'a>>::to_heap(v);
                    }
                }
            )*
        }

        unsafe impl<'a> $crate::HeapInline<'a> for $ref_type<'a> {
            type Storage = *mut $storage_type<'a>;

            fn to_heap(self) -> Self::Storage {
                self.0.ptr
            }

            unsafe fn from_heap(heap: &$crate::Heap<'a>, v: &Self::Storage) -> Self {
                $ref_type($crate::PinnedRef::new(heap, *v))
            }
        }

        impl<'a> $crate::GCRef for $ref_type<'a> {
            #[cfg(test)]
            fn address(&self) -> usize {
                unsafe { ::std::mem::transmute(self.0.ptr) }
            }
        }
    }
}

#[macro_export]
macro_rules! gc_inline_enum {
    { AS_ITEM $x:item } => { $x };

    {
        PARSE_VARIANTS $helper_name:ident
        {}
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name DONE
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name NO_FIELDS
            {}
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident , $($more_variants:tt)* }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name NO_FIELDS
            { $($more_variants)* }
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident ( $($field_types:tt)* ) }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name ( $($field_types)* )
            { }
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident ( $($field_types:tt)* ), $($more_variants:tt)* }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name ( $($field_types)* )
            { $($more_variants)* }
            $($etc)*
        }
    };

    {
        DECLARE_STORAGE_TYPE DONE { $($accumulated_output:tt)* } $storage_type:ident
    } => {
        gc_inline_enum! {
            AS_ITEM
            enum $storage_type<'a> {
                $($accumulated_output)*
            }
        }
    };

    {
        DECLARE_STORAGE_TYPE VARIANT $variant_name:ident NO_FIELDS
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $more_variants {
                $($accumulated_output)*
                $variant_name,
            }
            $storage_type
        }
    };

    {
        DECLARE_STORAGE_TYPE VARIANT $variant_name:ident ( $($field_type:ty),* )
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $more_variants {
                $($accumulated_output)*
                $variant_name($(<$field_type as $crate::HeapInline<'a>>::Storage),*),
            }
            $storage_type
        }
    };

    {
        IMPL_MARK DONE { $($accumulated_output:tt)* } $storage_type:ident
    } => {
        gc_inline_enum! {
            AS_ITEM
            unsafe impl<'a> $crate::Mark<'a> for $storage_type<'a> {
                unsafe fn mark(ptr: *mut $storage_type<'a>) {
                    match *ptr {
                        $($accumulated_output)*
                    }
                }
            }
        }
    };

    {
        IMPL_MARK VARIANT $name:ident NO_FIELDS
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $more_variants {
                $($accumulated_output)*
                $storage_type::$name => (),
            }
            $storage_type
        }
    };

    {
        IMPL_MARK VARIANT $name:ident ( $($field_type:ty),* )
        $($etc:tt)*
    } => {
        gc_inline_enum! {
            TYPES_TO_IDENTS ( $($field_type),*, ) () (a b c d e f g h i j k l m n o p q r s t u v w x y z)
            (IMPL_MARK CONTINUE_VARIANT $name $($etc)*)
        }
    };

    {
        IMPL_MARK CONTINUE_VARIANT $name:ident
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
        ( $(($binding:ident : $field_type:ty))* )
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $more_variants {
                $($accumulated_output)*
                $storage_type::$name ( $(ref mut $binding),* ) => {
                    $( $crate::Mark::mark($binding as *mut <$field_type as HeapInline<'a>>::Storage); )*
                }
            }
            $storage_type
        }
    };

    { TYPES_TO_IDENTS () ($(($binding:ident : $btype:ty))*) $_leftovers:tt ($($ctn:tt)*) } => {
        gc_inline_enum! { $($ctn)* ($(($binding : $btype))*) }
    };
    {
        TYPES_TO_IDENTS
        ($t:ty, $($ts:ty),*)
        ($(($binding:ident : $btype:ty))*)
        ($id:ident $($ids:tt)*)
        ($($ctn:tt)*)
    } => {
        gc_inline_enum! {
            TYPES_TO_IDENTS
            ($($ts),*)
            ($(($binding : $btype))* ($id : $t))
            ($($ids)*)
            ($($ctn)*)
        }
    };

    {
        pub enum $stack_type:ident / $storage_type:ident <'a>
            $variants:tt
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $variants {}
            $storage_type
        }

        gc_inline_enum! {
            AS_ITEM
            #[derive(Debug, Clone, PartialEq)]
            pub enum $stack_type<'a>
                $variants
        }

        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $variants {}
            $storage_type
        }
    }
}
<commit_msg>Streamline some UFCS calls.<commit_after>#[macro_export]
macro_rules! gc_ref_type {
    (pub struct $ref_type:ident / $storage_type:ident <'a> {
        $($field_name:ident / $field_setter_name:ident : $field_type: ty),*
    }) => {
        struct $storage_type <'a> {
            $($field_name: <$field_type as $crate::HeapInline<'a>>::Storage,)*
        }

        unsafe impl<'a> $crate::Mark<'a> for $storage_type<'a> {
            unsafe fn mark(ptr: *mut $storage_type<'a>) {
                if !$crate::Heap::get_mark_bit(ptr) {
                    $crate::Heap::set_mark_bit(ptr);
                    $(
                        $crate::Mark::mark(
                            &mut (*ptr).$field_name
                                as *mut <$field_type as $crate::HeapInline<'a>>::Storage);
                    )*
                }
            }
        }

        unsafe impl<'a> $crate::Mark<'a> for *mut $storage_type<'a> {
            unsafe fn mark(field_ptr: *mut *mut $storage_type<'a>) {
                let ptr = *field_ptr;
                if !ptr.is_null() {
                    Mark::mark(ptr);
                }
            }
        }

        #[allow(raw_pointer_derive)]
        #[derive(Clone, Debug, PartialEq)]
        pub struct $ref_type<'a>($crate::PinnedRef<'a, $storage_type<'a>>);

        impl<'a> $ref_type<'a> {
            $(
                pub fn $field_name(&self) -> $field_type {
                    let ptr = self.0.ptr;
                    unsafe {
                        HeapInline::from_heap(
                            &*self.0.heap,
                            &(*ptr).$field_name)
                    }
                }

                pub fn $field_setter_name(&self, v: $field_type) {
                    let ptr = self.0.ptr;
                    unsafe {
                        (*ptr).$field_name = HeapInline::to_heap(v);
                    }
                }
            )*
        }

        unsafe impl<'a> $crate::HeapInline<'a> for $ref_type<'a> {
            type Storage = *mut $storage_type<'a>;

            fn to_heap(self) -> Self::Storage {
                self.0.ptr
            }

            unsafe fn from_heap(heap: &$crate::Heap<'a>, v: &Self::Storage) -> Self {
                $ref_type($crate::PinnedRef::new(heap, *v))
            }
        }

        impl<'a> $crate::GCRef for $ref_type<'a> {
            #[cfg(test)]
            fn address(&self) -> usize {
                unsafe { ::std::mem::transmute(self.0.ptr) }
            }
        }
    }
}

#[macro_export]
macro_rules! gc_inline_enum {
    { AS_ITEM $x:item } => { $x };

    {
        PARSE_VARIANTS $helper_name:ident
        {}
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name DONE
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name NO_FIELDS
            {}
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident , $($more_variants:tt)* }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name NO_FIELDS
            { $($more_variants)* }
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident ( $($field_types:tt)* ) }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name ( $($field_types)* )
            { }
            $($etc)*
        }
    };

    {
        PARSE_VARIANTS $helper_name:ident
        { $variant_name:ident ( $($field_types:tt)* ), $($more_variants:tt)* }
        $( $etc:tt )*
    } => {
        gc_inline_enum! {
            $helper_name VARIANT $variant_name ( $($field_types)* )
            { $($more_variants)* }
            $($etc)*
        }
    };

    {
        DECLARE_STORAGE_TYPE DONE { $($accumulated_output:tt)* } $storage_type:ident
    } => {
        gc_inline_enum! {
            AS_ITEM
            enum $storage_type<'a> {
                $($accumulated_output)*
            }
        }
    };

    {
        DECLARE_STORAGE_TYPE VARIANT $variant_name:ident NO_FIELDS
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $more_variants {
                $($accumulated_output)*
                $variant_name,
            }
            $storage_type
        }
    };

    {
        DECLARE_STORAGE_TYPE VARIANT $variant_name:ident ( $($field_type:ty),* )
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $more_variants {
                $($accumulated_output)*
                $variant_name($(<$field_type as $crate::HeapInline<'a>>::Storage),*),
            }
            $storage_type
        }
    };

    {
        IMPL_MARK DONE { $($accumulated_output:tt)* } $storage_type:ident
    } => {
        gc_inline_enum! {
            AS_ITEM
            unsafe impl<'a> $crate::Mark<'a> for $storage_type<'a> {
                unsafe fn mark(ptr: *mut $storage_type<'a>) {
                    match *ptr {
                        $($accumulated_output)*
                    }
                }
            }
        }
    };

    {
        IMPL_MARK VARIANT $name:ident NO_FIELDS
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $more_variants {
                $($accumulated_output)*
                $storage_type::$name => (),
            }
            $storage_type
        }
    };

    {
        IMPL_MARK VARIANT $name:ident ( $($field_type:ty),* )
        $($etc:tt)*
    } => {
        gc_inline_enum! {
            TYPES_TO_IDENTS ( $($field_type),*, ) () (a b c d e f g h i j k l m n o p q r s t u v w x y z)
            (IMPL_MARK CONTINUE_VARIANT $name $($etc)*)
        }
    };

    {
        IMPL_MARK CONTINUE_VARIANT $name:ident
        $more_variants:tt
        { $($accumulated_output:tt)* }
        $storage_type:ident
        ( $(($binding:ident : $field_type:ty))* )
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $more_variants {
                $($accumulated_output)*
                $storage_type::$name ( $(ref mut $binding),* ) => {
                    $( $crate::Mark::mark($binding as *mut <$field_type as HeapInline<'a>>::Storage); )*
                }
            }
            $storage_type
        }
    };

    { TYPES_TO_IDENTS () ($(($binding:ident : $btype:ty))*) $_leftovers:tt ($($ctn:tt)*) } => {
        gc_inline_enum! { $($ctn)* ($(($binding : $btype))*) }
    };
    {
        TYPES_TO_IDENTS
        ($t:ty, $($ts:ty),*)
        ($(($binding:ident : $btype:ty))*)
        ($id:ident $($ids:tt)*)
        ($($ctn:tt)*)
    } => {
        gc_inline_enum! {
            TYPES_TO_IDENTS
            ($($ts),*)
            ($(($binding : $btype))* ($id : $t))
            ($($ids)*)
            ($($ctn)*)
        }
    };

    {
        pub enum $stack_type:ident / $storage_type:ident <'a>
            $variants:tt
    } => {
        gc_inline_enum! {
            PARSE_VARIANTS DECLARE_STORAGE_TYPE $variants {}
            $storage_type
        }

        gc_inline_enum! {
            AS_ITEM
            #[derive(Debug, Clone, PartialEq)]
            pub enum $stack_type<'a>
                $variants
        }

        gc_inline_enum! {
            PARSE_VARIANTS IMPL_MARK $variants {}
            $storage_type
        }
    }
}
<|endoftext|>