<commit_before>#![cfg(not(windows))]

use super::Key;
use super::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::*;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use crate::ansi;
use crate::NEWLINE;

use std::mem;
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use ::libc::{dup, SIGINT, SIGWINCH, c_int, c_ushort, c_ulong};
use crate::screen::Screen;

pub struct UnixScreen {
    tty: Terminal,
    start_line: u16,
    desired_rows: u16,
}

impl Screen for UnixScreen {
    fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![SIGWINCH as u8] {
                self.blank_entire_screen();
                return vec![Nothing];
            } else if bytes == vec![SIGINT as u8] {
                return vec![Control('g')];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }
}

impl UnixScreen {
    pub fn is_cygwin() -> bool {
        false
    }

    pub fn open_screen(desired_rows: u16) -> UnixScreen {
        let mut tty = Terminal::open_terminal();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        UnixScreen {
            tty,
            start_line,
            desired_rows,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.restore_tty();
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: RawFd,
    output: File,
    output_buf: Vec<u8>,
    original_stty_state: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

fn start_sigwinch_handler() {
    let signals = signal_hook::iterator::Signals::new(&[SIGWINCH, SIGINT]).unwrap();
    thread::spawn(move || {
        for signal in signals.forever() {
            get_global_tx().send(vec![signal as u8]).unwrap();
        }
    });
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        start_sigwinch_handler();

        let mut ret = Terminal {
            input: rx,
            input_fd,
            output: output_file,
            output_buf: Vec::new(),
            original_stty_state: Vec::new(),
        };
        let current_stty_state = ret.stty(&["-g"]);
        ret.original_stty_state = current_stty_state;
        ret.initialize();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        ret
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn restore_tty(&mut self) {
        let state = String::from_utf8(self.original_stty_state.clone()).unwrap();
        self.stty(&[&state]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let tty_input = unsafe { Stdio::from_raw_fd(dup(self.input_fd)) };
        let mut process = match Command::new("/bin/stty")
            .args(args)
            .stdin(tty_input)
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            crate::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!(String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[5~" { return vec![PgUp] }
        if bytes == b"\x1B[6~" { return vec![PgDown] }
        if bytes == b"\x1B[H" { return vec![Home] }
        if bytes == b"\x1B[F" { return vec![End] }
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char == 27 {
            Control('g')
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.flush();
        self.restore_tty();
    }
}

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

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<commit_msg>Fix Rust 2021 compatibility warning<commit_after>#![cfg(not(windows))]

use super::Key;
use super::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::*;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use crate::ansi;
use crate::NEWLINE;

use std::mem;
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use ::libc::{dup, SIGINT, SIGWINCH, c_int, c_ushort, c_ulong};
use crate::screen::Screen;

pub struct UnixScreen {
    tty: Terminal,
    start_line: u16,
    desired_rows: u16,
}

impl Screen for UnixScreen {
    fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![SIGWINCH as u8] {
                self.blank_entire_screen();
                return vec![Nothing];
            } else if bytes == vec![SIGINT as u8] {
                return vec![Control('g')];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }
}

impl UnixScreen {
    pub fn is_cygwin() -> bool {
        false
    }

    pub fn open_screen(desired_rows: u16) -> UnixScreen {
        let mut tty = Terminal::open_terminal();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        UnixScreen {
            tty,
            start_line,
            desired_rows,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.restore_tty();
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: RawFd,
    output: File,
    output_buf: Vec<u8>,
    original_stty_state: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

fn start_sigwinch_handler() {
    let signals = signal_hook::iterator::Signals::new(&[SIGWINCH, SIGINT]).unwrap();
    thread::spawn(move || {
        for signal in signals.forever() {
            get_global_tx().send(vec![signal as u8]).unwrap();
        }
    });
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        start_sigwinch_handler();

        let mut ret = Terminal {
            input: rx,
            input_fd,
            output: output_file,
            output_buf: Vec::new(),
            original_stty_state: Vec::new(),
        };
        let current_stty_state = ret.stty(&["-g"]);
        ret.original_stty_state = current_stty_state;
        ret.initialize();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        ret
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn restore_tty(&mut self) {
        let state = String::from_utf8(self.original_stty_state.clone()).unwrap();
        self.stty(&[&state]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let tty_input = unsafe { Stdio::from_raw_fd(dup(self.input_fd)) };
        let mut process = match Command::new("/bin/stty")
            .args(args)
            .stdin(tty_input)
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            crate::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!("stty invocation failed: {}", String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[5~" { return vec![PgUp] }
        if bytes == b"\x1B[6~" { return vec![PgDown] }
        if bytes == b"\x1B[H" { return vec![Home] }
        if bytes == b"\x1B[F" { return vec![End] }
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char == 27 {
            Control('g')
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.flush();
        self.restore_tty();
    }
}

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

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<|endoftext|>


<commit_before>// A tuple struct that can be compared
#[deriving(PartialEq, PartialOrd)]
struct Centimeters(f64);

// A tuple struct that can be printed
#[deriving(Show)]
struct Inches(int);

impl Inches {
    fn to_centimeters(&self) -> Centimeters {
        let &Inches(inches) = self;

        Centimeters(inches as f64 * 2.54)
    }
}

// A vanilla tuple struct
struct Seconds(int);

fn main() {
    let _one_second = Seconds(1);

    // Error! `Seconds` can't be printed, because it doesn't implement the
    // `Show` trait
    println!("One second looks like: {}", _one_second);
    // FIXME ^ Add `#[deriving(Show)]` to `Seconds`

    // Error: `Seconds` can't be compared, because it doesn't implement the
    // `PartialEq` trait
    let _this_is_true = _one_second == _one_second;
    // FIXME ^ Add `#[deriving(PartialEq)]` to `Seconds`

    let foot = Inches(12);

    println!("One foot === {}", foot);

    let meter = Centimeters(100.0);

    let cmp =
        if foot.to_centimeters() < meter {
            "smaller"
        } else {
            "bigger"
        };

    println!("one foot is {} than one meter", cmp);
}
<commit_msg>trait: replace FIXMEs by TODOs<commit_after>// A tuple struct that can be compared
#[deriving(PartialEq, PartialOrd)]
struct Centimeters(f64);

// A tuple struct that can be printed
#[deriving(Show)]
struct Inches(int);

impl Inches {
    fn to_centimeters(&self) -> Centimeters {
        let &Inches(inches) = self;

        Centimeters(inches as f64 * 2.54)
    }
}

// A vanilla tuple struct
struct Seconds(int);

fn main() {
    let _one_second = Seconds(1);

    // Error! `Seconds` can't be printed, because it doesn't implement the
    // `Show` trait
    //println!("One second looks like: {}", _one_second);
    // TODO ^ Try uncommenting this line

    // Error: `Seconds` can't be compared, because it doesn't implement the
    // `PartialEq` trait
    //let _this_is_true = _one_second == _one_second;
    // TODO ^ Try uncommenting this line

    let foot = Inches(12);

    println!("One foot === {}", foot);

    let meter = Centimeters(100.0);

    let cmp =
        if foot.to_centimeters() < meter {
            "smaller"
        } else {
            "bigger"
        };

    println!("one foot is {} than one meter", cmp);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::future::Future;
use std::sync::{Mutex, Arc};
use std::time::Duration;

use async_std::{fs, path::{Path, PathBuf}, task};

use http::status::StatusCode;

use serde::{Deserialize, Serialize};

use tide::{prelude::*, IntoResponse, Request, Response, ResultExt, Server};

struct GlobalStorage {
    database: Arc<Mutex<Database>>
}

struct Database {
    message_storage: HashMap<usize, Message>,
    static_file_server: StaticFileHandler
}

#[derive(Serialize, Deserialize, Clone)]
struct Message {
    author: Option<String>,
    contents: String
}

impl GlobalStorage {
    async fn new(root_path: &str) -> std::io::Result<GlobalStorage> {
        Ok(GlobalStorage {
            database: Arc::new(Mutex::new(Database {
                message_storage: Default::default(),
                static_file_server: StaticFileHandler::new(root_path).await?
            }))
        })
    }

    fn set(&self, id: usize, msg: Message) {
        let contents = &mut self.database.lock().unwrap().message_storage;

        if contents.contains_key(&id) {
            contents.entry(id).and_modify(|v| *v = msg);
            return
        }

        contents.insert(id, msg);
    }

    fn get(&self, id: usize) -> Option<Message> {
        let contents = &self.database.lock().unwrap().message_storage;

        match contents.get(&id) {
            Some(v) => Some(v.clone()),
            None => None
        }
    }

    fn clone(&self) -> GlobalStorage {
        GlobalStorage {
            database: self.database.clone()
        }
    }

    async fn delay(&self) {
        println!("delaying...");
        task::sleep(Duration::from_secs(3)).await;
        println!("done...");
    }

}

async fn handle_post(mut req: Request<GlobalStorage>) -> Response {
    let msg = match req.body_json().await.client_err() {
        Ok(v) => v,
        Err(e) => {
            println!("body json parse failed. e = {:#?}", e);
            return e.into_response()
        }
    };
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => {
            println!("param id is not found. {:#?}", e);
            return e.into_response()
        }
    };

    req.state().set(id, msg);
    Response::new(StatusCode::OK.into()).body_string("".to_owned())
}

async fn handle_get(req: Request<GlobalStorage>) -> Response {
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };

    if let Some(msg) = req.state().get(id) {
        match Response::new(StatusCode::OK.into()).body_json(&msg) {
            Ok(v) => v,
            _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())

        }
    }
    else {
        Response::new(StatusCode::NOT_FOUND.into())
    }
}

async fn handle_delay(req: Request<GlobalStorage>) -> Response {
    let cloned_state = {
        req.state().clone()
    };

    cloned_state.delay().await;
    Response::new(StatusCode::OK.into()).body_string("ok.".to_owned())
}

/*
struct Database {
    contents: Mutex<HashMap<usize, Message>>,
    static_file_server: Mutex<StaticFileHandler>
}

#[derive(Serialize, Deserialize, Clone)]
struct Message {
    author: Option<String>,
    contents: String
}

impl Database {
    async fn new(root_path: &str) -> std::io::Result<Database> {
        Ok(Database {
            contents: Default::default(),
            static_file_server: Mutex::new(StaticFileHandler::new(root_path).await?)
        })
    }

    fn set(&self, id: usize, msg: Message) {
        let mut contents = self.contents.lock().unwrap();

        if contents.contains_key(&id) {
            contents.entry(id).and_modify(|v| *v = msg);
            return
        }

        contents.insert(id, msg);
    }

    fn get(&self, id: usize) -> Option<Message> {
        let mut contents = self.contents.lock().unwrap();

        match contents.get(&id) {
            Some(v) => Some(v.clone()),
            None => None
        }
    }
}
*/

struct StaticFileHandler {
    root_path: PathBuf,
    file_cache: HashMap<PathBuf, Arc<Mutex<String>>>
}

impl StaticFileHandler {
    async fn new(root_path: &str) -> std::io::Result<StaticFileHandler> {
        let abs_root_path = fs::canonicalize(root_path).await?;

        Ok(StaticFileHandler {
            root_path : abs_root_path,
            file_cache : Default::default()
        })
    }

    /*
    async fn check_valid_path(&self, input_path: &Path) -> std::io::Result<bool> {
        let abs_input_path = fs::canonicalize(input_path).await?;

        Ok(abs_input_path.as_path().starts_with(&self.root_path))
    }

    async fn cache_file(&mut self, input_path: &Path) -> std::io::Result<bool> {
        debug_assert!(self.check_valid_path(input_path).await?);

        // 1. load file
        let new_file_data = Arc::new(Mutex::new(fs::read_to_string(input_path).await?));

        // 2. build key
        let abs_input_path = fs::canonicalize(input_path).await?;

        // 3. set key value
        if self.file_cache.contains_key(&abs_input_path) {
            self.file_cache.entry(abs_input_path).and_modify(|e| *e = new_file_data);
        }
        else {
            self.file_cache.insert(abs_input_path, new_file_data);
        }

        Ok(true)
    }

    async fn get_cached_file(&self, input_path: &Path) -> std::io::Result<Arc<Mutex<String>>> {
        if !self.check_valid_path(input_path).await? {
            return Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }

        let abs_input_path = fs::canonicalize(input_path).await?;

        if !self.file_cache.contains_key(&abs_input_path) {
            self.cache_file(&abs_input_path).await?;
        }

        match self.file_cache.get(&abs_input_path) {
            Some(v) => Ok(v.clone()),
            None => Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }
    }
    */
}

/*
async fn handle_post(mut req: Request<Database>) -> Response {
    let mut msg = match req.body_json().await.client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) =>  return e.into_response()
    };

    req.state().set(id, msg);
    Response::new(StatusCode::OK.into()).body_string("".to_owned())
}

async fn handle_get(mut req: Request<Database>) -> Response {
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };

    if let Some(msg) = req.state().get(id) {
        match Response::new(StatusCode::OK.into()).body_json(&msg) {
            Ok(v) => v,
            _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())

        }
    }
    else {
        Response::new(StatusCode::NOT_FOUND.into())
    }
}

async fn handle_get_static(mut req: Request<Database>) -> Response {
    let rel_path_buf = req.uri().path().to_owned();
    let rel_path = Path::new(&rel_path_buf[7..]);

    let mut sfs = req.state().static_file_server.lock().unwrap();

    match sfs.get_cached_file(&rel_path).await {
        //Ok(v) => Response::new(StatusCode::OK.into()).body_string(*v),
        Ok(v) => Response::new(StatusCode::OK.into()),
        _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())
    }
}

*/

#[async_std::main]
async fn main() -> std::io::Result<()> {
    let database = GlobalStorage::new("./static_files/").await?;

    let mut app = Server::with_state(database);
    app.at("/").get(|_| async move { "Hello, world!" });
    app.at("/message/:id").post(handle_post).get(handle_get);
    app.at("/delay").get(handle_delay);
    //app.at("/static/*").get(handle_get_static);
    app.listen("127.0.0.1:9876").await?;
    Ok(())
}
<commit_msg>Rust_Tide: resolving 'future is not send.' problem'<commit_after>use std::collections::HashMap;
use std::future::Future;
use std::sync::{Mutex, Arc};
use std::time::Duration;

use async_std::{fs, path::{Path, PathBuf}, task};

use http::status::StatusCode;

use serde::{Deserialize, Serialize};

use tide::{prelude::*, IntoResponse, Request, Response, ResultExt, Server};

struct GlobalStorage {
    database: Arc<Mutex<Database>>
}

struct Database {
    message_storage: HashMap<usize, Message>,
    static_file_server: StaticFileHandler
}

#[derive(Serialize, Deserialize, Clone)]
struct Message {
    author: Option<String>,
    contents: String
}

impl GlobalStorage {
    async fn new(root_path: &str) -> std::io::Result<GlobalStorage> {
        Ok(GlobalStorage {
            database: Arc::new(Mutex::new(Database {
                message_storage: Default::default(),
                static_file_server: StaticFileHandler::new(root_path).await?
            }))
        })
    }

    fn clone(&self) -> GlobalStorage {
        GlobalStorage {
            database: self.database.clone()
        }
    }

    async fn set_async(&self, id: usize, msg: Message) {
        let contents = &mut self.database.lock().unwrap().message_storage;

        if contents.contains_key(&id) {
            contents.entry(id).and_modify(|v| *v = msg);
            return
        }

        contents.insert(id, msg);
    }

    async fn get_async(&self, id: usize) -> Option<Message> {
        let contents = &self.database.lock().unwrap().message_storage;

        match contents.get(&id) {
            Some(v) => Some(v.clone()),
            None => None
        }
    }

    async fn delay(&self) {
        println!("delaying...");
        task::sleep(Duration::from_secs(3)).await;
        println!("done...");
    }

}

async fn handle_msg_get(req: Request<GlobalStorage>) -> Response {
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };

    let cloned_state = { req.state().clone() };

    if let Some(msg) = cloned_state.get_async(id).await {
        match Response::new(StatusCode::OK.into()).body_json(&msg) {
            Ok(v) => v,
            _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())

        }
    }
    else {
        Response::new(StatusCode::NOT_FOUND.into())
    }
}

async fn handle_msg_post(mut req: Request<GlobalStorage>) -> Response {
    let msg = match req.body_json().await.client_err() {
        Ok(v) => v,
        Err(e) => {
            println!("body json parse failed. e = {:#?}", e);
            return e.into_response()
        }
    };
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => {
            println!("param id is not found. {:#?}", e);
            return e.into_response()
        }
    };

    let cloned_state = { req.state().clone() };

    cloned_state.set_async(id, msg).await;
    Response::new(StatusCode::OK.into()).body_string("".to_owned())
}

async fn handle_delay(req: Request<GlobalStorage>) -> Response {
    let cloned_state = {
        req.state().clone()
    };

    cloned_state.delay().await;
    Response::new(StatusCode::OK.into()).body_string("ok.".to_owned())
}

struct StaticFileHandler {
    root_path: PathBuf,
    file_cache: HashMap<PathBuf, Arc<Mutex<String>>>
}

impl StaticFileHandler {
    async fn new(root_path: &str) -> std::io::Result<StaticFileHandler> {
        let abs_root_path = fs::canonicalize(root_path).await?;

        Ok(StaticFileHandler {
            root_path : abs_root_path,
            file_cache : Default::default()
        })
    }

/*
    async fn check_valid_path(&self, input_path: &Path) -> std::io::Result<bool> {
        let abs_input_path = fs::canonicalize(input_path).await?;

        Ok(abs_input_path.as_path().starts_with(&self.root_path))
    }

    async fn cache_file(&mut self, input_path: &Path) -> std::io::Result<bool> {
        debug_assert!(self.check_valid_path(input_path).await?);

        // 1. load file
        let new_file_data = Arc::new(Mutex::new(fs::read_to_string(input_path).await?));

        // 2. build key
        let abs_input_path = fs::canonicalize(input_path).await?;

        // 3. set key value
        if self.file_cache.contains_key(&abs_input_path) {
            self.file_cache.entry(abs_input_path).and_modify(|e| *e = new_file_data);
        }
        else {
            self.file_cache.insert(abs_input_path, new_file_data);
        }

        Ok(true)
    }

    async fn get_cached_file(&self, input_path: &Path) -> std::io::Result<Arc<Mutex<String>>> {
        if !self.check_valid_path(input_path).await? {
            return Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }

        let abs_input_path = fs::canonicalize(input_path).await?;

        if !self.file_cache.contains_key(&abs_input_path) {
            self.cache_file(&abs_input_path).await?;
        }

        match self.file_cache.get(&abs_input_path) {
            Some(v) => Ok(v.clone()),
            None => Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }
    }
    */
}

/*
async fn handle_get_static(req: Request<GlobalStorage>) -> Response {
    let rel_path_buf = req.uri().path().to_owned();
    let rel_path = Path::new(&rel_path_buf[7..]);

    let mut cloned_database = {
        req.state().database.clone()
    };

    match cloned_database.lock().unwrap().static_file_server.get_cached_file(&rel_path).await {
        //Ok(v) => Response::new(StatusCode::OK.into()).body_string(*v),
        Ok(v) => Response::new(StatusCode::OK.into()),
        _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())
    }
}
*/

#[async_std::main]
async fn main() -> std::io::Result<()> {
    let database = GlobalStorage::new("./static_files/").await?;

    let mut app = Server::with_state(database);
    app.at("/").get(|_| async move { "Hello, world!" });
    //app.at("/message/:id").post(handle_post).get(handle_get);
    app.at("/msg/:id").post(handle_msg_post).get(handle_msg_get);
    app.at("/delay").get(handle_delay);
    //app.at("/static/*").get(handle_get_static);
    app.listen("127.0.0.1:9876").await?;
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use super::transport::RaftStoreRouter;
use super::RaftKv;
use super::Result;
use crate::import::SSTImporter;
use crate::pd::{Error as PdError, PdClient, PdTask, INVALID_ID};
use crate::raftstore::coprocessor::dispatcher::CoprocessorHost;
use crate::raftstore::store::fsm::store::StoreMeta;
use crate::raftstore::store::fsm::{RaftBatchSystem, RaftRouter};
use crate::raftstore::store::{
    self, initial_region, keys, Config as StoreConfig, SnapManager, Transport,
};
use crate::server::readpool::ReadPool;
use crate::server::Config as ServerConfig;
use crate::server::ServerRaftStoreRouter;
use crate::storage::lock_manager::{DetectorScheduler, WaiterMgrScheduler};
use crate::storage::{Config as StorageConfig, Storage};
use engine::rocks::DB;
use engine::Engines;
use engine::Peekable;
use kvproto::metapb;
use kvproto::raft_serverpb::StoreIdent;
use tikv_util::worker::FutureWorker;

const MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT: u64 = 60;
const CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS: u64 = 3;

/// Creates a new storage engine which is backed by the Raft consensus
/// protocol.
pub fn create_raft_storage<S>(
    engine: RaftKv<S>,
    cfg: &StorageConfig,
    read_pool: ReadPool,
    local_storage: Option<Arc<DB>>,
    raft_store_router: Option<ServerRaftStoreRouter>,
    waiter_mgr_scheduler: Option<WaiterMgrScheduler>,
    detector_scheduler: Option<DetectorScheduler>,
) -> Result<Storage<RaftKv<S>>>
where
    S: RaftStoreRouter + 'static,
{
    let store = Storage::from_engine(
        engine,
        cfg,
        read_pool,
        local_storage,
        raft_store_router,
        waiter_mgr_scheduler,
        detector_scheduler,
    )?;
    Ok(store)
}

/// A wrapper for the raftstore which runs Multi-Raft.
// TODO: we will rename another better name like RaftStore later.
pub struct Node<C: PdClient + 'static> {
    cluster_id: u64,
    store: metapb::Store,
    store_cfg: StoreConfig,
    system: RaftBatchSystem,
    has_started: bool,

    pd_client: Arc<C>,
}

impl<C> Node<C>
where
    C: PdClient,
{
    /// Creates a new Node.
    pub fn new(
        system: RaftBatchSystem,
        cfg: &ServerConfig,
        store_cfg: &StoreConfig,
        pd_client: Arc<C>,
    ) -> Node<C> {
        let mut store = metapb::Store::default();
        store.set_id(INVALID_ID);
        if cfg.advertise_addr.is_empty() {
            store.set_address(cfg.addr.clone());
        } else {
            store.set_address(cfg.advertise_addr.clone())
        }
        store.set_version(env!("CARGO_PKG_VERSION").to_string());

        let mut labels = Vec::new();
        for (k, v) in &cfg.labels {
            let mut label = metapb::StoreLabel::default();
            label.set_key(k.to_owned());
            label.set_value(v.to_owned());
            labels.push(label);
        }
        store.set_labels(labels.into());

        Node {
            cluster_id: cfg.cluster_id,
            store,
            store_cfg: store_cfg.clone(),
            pd_client,
            system,
            has_started: false,
        }
    }

    /// Starts the Node. It tries to bootstrap cluster if the cluster is not
    /// bootstrapped yet. Then it spawns a thread to run the raftstore in
    /// background.
    #[allow(clippy::too_many_arguments)]
    pub fn start<T>(
        &mut self,
        engines: Engines,
        trans: T,
        snap_mgr: SnapManager,
        pd_worker: FutureWorker<PdTask>,
        store_meta: Arc<Mutex<StoreMeta>>,
        coprocessor_host: CoprocessorHost,
        importer: Arc<SSTImporter>,
    ) -> Result<()>
    where
        T: Transport + 'static,
    {
        let mut store_id = self.check_store(&engines)?;
        if store_id == INVALID_ID {
            store_id = self.bootstrap_store(&engines)?;
            fail_point!("node_after_bootstrap_store", |_| Err(box_err!(
                "injected error: node_after_bootstrap_store"
            )));
        }
        self.store.set_id(store_id);
        {
            let mut meta = store_meta.lock().unwrap();
            meta.store_id = Some(store_id);
        }
        if let Some(first_region) = self.check_or_prepare_bootstrap_cluster(&engines, store_id)? {
            debug!("try bootstrap cluster"; "store_id" => store_id, "region" => ?first_region);
            // cluster is not bootstrapped, and we choose first store to bootstrap
            fail_point!("node_after_prepare_bootstrap_cluster", |_| Err(box_err!(
                "injected error: node_after_prepare_bootstrap_cluster"
            )));
            self.bootstrap_cluster(&engines, first_region)?;
        }

        // Put store only if the cluster is bootstrapped.
        self.pd_client.put_store(self.store.clone())?;

        self.start_store(
            store_id,
            engines,
            trans,
            snap_mgr,
            pd_worker,
            store_meta,
            coprocessor_host,
            importer,
        )?;
        Ok(())
    }

    /// Gets the store id.
    pub fn id(&self) -> u64 {
        self.store.get_id()
    }

    /// Gets a transmission end of a channel which is used to send `Msg` to the
    /// raftstore.
    pub fn get_router(&self) -> RaftRouter {
        self.system.router()
    }

    // check store, return store id for the engine.
    // If the store is not bootstrapped, use INVALID_ID.
    fn check_store(&self, engines: &Engines) -> Result<u64> {
        let res = engines.kv.get_msg::<StoreIdent>(keys::STORE_IDENT_KEY)?;
        if res.is_none() {
            return Ok(INVALID_ID);
        }

        let ident = res.unwrap();
        if ident.get_cluster_id() != self.cluster_id {
            return Err(box_err!(
                "cluster ID mismatch, local {} != remote {}, \
                 you are trying to connect to another cluster, please reconnect to the correct PD",
                ident.get_cluster_id(),
                self.cluster_id
            ));
        }

        let store_id = ident.get_store_id();
        if store_id == INVALID_ID {
            return Err(box_err!("invalid store ident {:?}", ident));
        }
        Ok(store_id)
    }

    fn alloc_id(&self) -> Result<u64> {
        let id = self.pd_client.alloc_id()?;
        Ok(id)
    }

    fn bootstrap_store(&self, engines: &Engines) -> Result<u64> {
        let store_id = self.alloc_id()?;
        debug!("alloc store id"; "store_id" => store_id);

        store::bootstrap_store(engines, self.cluster_id, store_id)?;

        Ok(store_id)
    }

    // Exported for tests.
    #[doc(hidden)]
    pub fn prepare_bootstrap_cluster(
        &self,
        engines: &Engines,
        store_id: u64,
    ) -> Result<metapb::Region> {
        let region_id = self.alloc_id()?;
        debug!(
            "alloc first region id";
            "region_id" => region_id,
            "cluster_id" => self.cluster_id,
            "store_id" => store_id
        );
        let peer_id = self.alloc_id()?;
        debug!(
            "alloc first peer id for first region";
            "peer_id" => peer_id,
            "region_id" => region_id,
        );

        let region = initial_region(store_id, region_id, peer_id);
        store::prepare_bootstrap_cluster(engines, &region)?;
        Ok(region)
    }

    fn check_or_prepare_bootstrap_cluster(
        &self,
        engines: &Engines,
        store_id: u64,
    ) -> Result<Option<metapb::Region>> {
        if let Some(first_region) = engines.kv.get_msg(keys::PREPARE_BOOTSTRAP_KEY)? {
            Ok(Some(first_region))
        } else {
            if self.check_cluster_bootstrapped()? {
                Ok(None)
            } else {
                self.prepare_bootstrap_cluster(engines, store_id).map(Some)
            }
        }
    }

    fn bootstrap_cluster(&mut self, engines: &Engines, first_region: metapb::Region) -> Result<()> {
        let region_id = first_region.get_id();
        let mut retry = 0;
        while retry < MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT {
            match self
                .pd_client
                .bootstrap_cluster(self.store.clone(), first_region.clone())
            {
                Ok(_) => {
                    info!("bootstrap cluster ok"; "cluster_id" => self.cluster_id);
                    fail_point!("node_after_bootstrap_cluster", |_| Err(box_err!(
                        "injected error: node_after_prepare_bootstrap_cluster"
                    )));
                    store::clear_prepare_bootstrap_key(engines)?;
                    return Ok(());
                }
                Err(PdError::ClusterBootstrapped(_)) => match self.pd_client.get_region(b"") {
                    Ok(region) => {
                        if region == first_region {
                            store::clear_prepare_bootstrap_key(engines)?;
                            return Ok(());
                        } else {
                            info!("cluster is already bootstrapped"; "cluster_id" => self.cluster_id);
                            store::clear_prepare_bootstrap_cluster(engines, region_id)?;
                            return Ok(());
                        }
                    }
                    Err(e) => {
                        warn!("get the first region failed"; "err" => ?e);
                    }
                },
                // TODO: should we clean region for other errors too?
                Err(e) => {
                    error!("bootstrap cluster"; "cluster_id" => self.cluster_id, "error" => ?e)
                }
            }
            retry += 1;
            thread::sleep(Duration::from_secs(
                CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS,
            ));
        }
        Err(box_err!("bootstrapped cluster failed"))
    }

    fn check_cluster_bootstrapped(&self) -> Result<bool> {
        for _ in 0..MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT {
            match self.pd_client.is_cluster_bootstrapped() {
                Ok(b) => return Ok(b),
                Err(e) => {
                    warn!("check cluster bootstrapped failed"; "err" => ?e);
                }
            }
            thread::sleep(Duration::from_secs(
                CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS,
            ));
        }
        Err(box_err!("check cluster bootstrapped failed"))
    }

    #[allow(clippy::too_many_arguments)]
    fn start_store<T>(
        &mut self,
        store_id: u64,
        engines: Engines,
        trans: T,
        snap_mgr: SnapManager,
        pd_worker: FutureWorker<PdTask>,
        store_meta: Arc<Mutex<StoreMeta>>,
        coprocessor_host: CoprocessorHost,
        importer: Arc<SSTImporter>,
    ) -> Result<()>
    where
        T: Transport + 'static,
    {
        info!("start raft store thread"; "store_id" => store_id);

        if self.has_started {
            return Err(box_err!("{} is already started", store_id));
        }
        self.has_started = true;
        let cfg = self.store_cfg.clone();
        let pd_client = Arc::clone(&self.pd_client);
        let store = self.store.clone();
        self.system.spawn(
            store,
            cfg,
            engines,
            trans,
            pd_client,
            snap_mgr,
            pd_worker,
            store_meta,
            coprocessor_host,
            importer,
        )?;
        Ok(())
    }

    fn stop_store(&mut self, store_id: u64) {
        info!("stop raft store thread"; "store_id" => store_id);
        self.system.shutdown();
    }

    /// Stops the Node.
    pub fn stop(&mut self) {
        let store_id = self.store.get_id();
        self.stop_store(store_id)
    }
}
<commit_msg>Info log for cluster bootstrap (#5172)<commit_after>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use super::transport::RaftStoreRouter;
use super::RaftKv;
use super::Result;
use crate::import::SSTImporter;
use crate::pd::{Error as PdError, PdClient, PdTask, INVALID_ID};
use crate::raftstore::coprocessor::dispatcher::CoprocessorHost;
use crate::raftstore::store::fsm::store::StoreMeta;
use crate::raftstore::store::fsm::{RaftBatchSystem, RaftRouter};
use crate::raftstore::store::{
    self, initial_region, keys, Config as StoreConfig, SnapManager, Transport,
};
use crate::server::readpool::ReadPool;
use crate::server::Config as ServerConfig;
use crate::server::ServerRaftStoreRouter;
use crate::storage::lock_manager::{DetectorScheduler, WaiterMgrScheduler};
use crate::storage::{Config as StorageConfig, Storage};
use engine::rocks::DB;
use engine::Engines;
use engine::Peekable;
use kvproto::metapb;
use kvproto::raft_serverpb::StoreIdent;
use tikv_util::worker::FutureWorker;

const MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT: u64 = 60;
const CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS: u64 = 3;

/// Creates a new storage engine which is backed by the Raft consensus
/// protocol.
pub fn create_raft_storage<S>(
    engine: RaftKv<S>,
    cfg: &StorageConfig,
    read_pool: ReadPool,
    local_storage: Option<Arc<DB>>,
    raft_store_router: Option<ServerRaftStoreRouter>,
    waiter_mgr_scheduler: Option<WaiterMgrScheduler>,
    detector_scheduler: Option<DetectorScheduler>,
) -> Result<Storage<RaftKv<S>>>
where
    S: RaftStoreRouter + 'static,
{
    let store = Storage::from_engine(
        engine,
        cfg,
        read_pool,
        local_storage,
        raft_store_router,
        waiter_mgr_scheduler,
        detector_scheduler,
    )?;
    Ok(store)
}

/// A wrapper for the raftstore which runs Multi-Raft.
// TODO: we will rename another better name like RaftStore later.
pub struct Node<C: PdClient + 'static> {
    cluster_id: u64,
    store: metapb::Store,
    store_cfg: StoreConfig,
    system: RaftBatchSystem,
    has_started: bool,

    pd_client: Arc<C>,
}

impl<C> Node<C>
where
    C: PdClient,
{
    /// Creates a new Node.
    pub fn new(
        system: RaftBatchSystem,
        cfg: &ServerConfig,
        store_cfg: &StoreConfig,
        pd_client: Arc<C>,
    ) -> Node<C> {
        let mut store = metapb::Store::default();
        store.set_id(INVALID_ID);
        if cfg.advertise_addr.is_empty() {
            store.set_address(cfg.addr.clone());
        } else {
            store.set_address(cfg.advertise_addr.clone())
        }
        store.set_version(env!("CARGO_PKG_VERSION").to_string());

        let mut labels = Vec::new();
        for (k, v) in &cfg.labels {
            let mut label = metapb::StoreLabel::default();
            label.set_key(k.to_owned());
            label.set_value(v.to_owned());
            labels.push(label);
        }
        store.set_labels(labels.into());

        Node {
            cluster_id: cfg.cluster_id,
            store,
            store_cfg: store_cfg.clone(),
            pd_client,
            system,
            has_started: false,
        }
    }

    /// Starts the Node. It tries to bootstrap cluster if the cluster is not
    /// bootstrapped yet. Then it spawns a thread to run the raftstore in
    /// background.
    #[allow(clippy::too_many_arguments)]
    pub fn start<T>(
        &mut self,
        engines: Engines,
        trans: T,
        snap_mgr: SnapManager,
        pd_worker: FutureWorker<PdTask>,
        store_meta: Arc<Mutex<StoreMeta>>,
        coprocessor_host: CoprocessorHost,
        importer: Arc<SSTImporter>,
    ) -> Result<()>
    where
        T: Transport + 'static,
    {
        let mut store_id = self.check_store(&engines)?;
        if store_id == INVALID_ID {
            store_id = self.bootstrap_store(&engines)?;
            fail_point!("node_after_bootstrap_store", |_| Err(box_err!(
                "injected error: node_after_bootstrap_store"
            )));
        }
        self.store.set_id(store_id);
        {
            let mut meta = store_meta.lock().unwrap();
            meta.store_id = Some(store_id);
        }
        if let Some(first_region) = self.check_or_prepare_bootstrap_cluster(&engines, store_id)? {
            info!("trying to bootstrap cluster"; "store_id" => store_id, "region" => ?first_region);
            // cluster is not bootstrapped, and we choose first store to bootstrap
            fail_point!("node_after_prepare_bootstrap_cluster", |_| Err(box_err!(
                "injected error: node_after_prepare_bootstrap_cluster"
            )));
            self.bootstrap_cluster(&engines, first_region)?;
        }

        // Put store only if the cluster is bootstrapped.
        self.pd_client.put_store(self.store.clone())?;

        self.start_store(
            store_id,
            engines,
            trans,
            snap_mgr,
            pd_worker,
            store_meta,
            coprocessor_host,
            importer,
        )?;
        Ok(())
    }

    /// Gets the store id.
    pub fn id(&self) -> u64 {
        self.store.get_id()
    }

    /// Gets a transmission end of a channel which is used to send `Msg` to the
    /// raftstore.
    pub fn get_router(&self) -> RaftRouter {
        self.system.router()
    }

    // check store, return store id for the engine.
    // If the store is not bootstrapped, use INVALID_ID.
    fn check_store(&self, engines: &Engines) -> Result<u64> {
        let res = engines.kv.get_msg::<StoreIdent>(keys::STORE_IDENT_KEY)?;
        if res.is_none() {
            return Ok(INVALID_ID);
        }

        let ident = res.unwrap();
        if ident.get_cluster_id() != self.cluster_id {
            return Err(box_err!(
                "cluster ID mismatch, local {} != remote {}, \
                 you are trying to connect to another cluster, please reconnect to the correct PD",
                ident.get_cluster_id(),
                self.cluster_id
            ));
        }

        let store_id = ident.get_store_id();
        if store_id == INVALID_ID {
            return Err(box_err!("invalid store ident {:?}", ident));
        }
        Ok(store_id)
    }

    fn alloc_id(&self) -> Result<u64> {
        let id = self.pd_client.alloc_id()?;
        Ok(id)
    }

    fn bootstrap_store(&self, engines: &Engines) -> Result<u64> {
        let store_id = self.alloc_id()?;
        debug!("alloc store id"; "store_id" => store_id);

        store::bootstrap_store(engines, self.cluster_id, store_id)?;

        Ok(store_id)
    }

    // Exported for tests.
    #[doc(hidden)]
    pub fn prepare_bootstrap_cluster(
        &self,
        engines: &Engines,
        store_id: u64,
    ) -> Result<metapb::Region> {
        let region_id = self.alloc_id()?;
        debug!(
            "alloc first region id";
            "region_id" => region_id,
            "cluster_id" => self.cluster_id,
            "store_id" => store_id
        );
        let peer_id = self.alloc_id()?;
        debug!(
            "alloc first peer id for first region";
            "peer_id" => peer_id,
            "region_id" => region_id,
        );

        let region = initial_region(store_id, region_id, peer_id);
        store::prepare_bootstrap_cluster(engines, &region)?;
        Ok(region)
    }

    fn check_or_prepare_bootstrap_cluster(
        &self,
        engines: &Engines,
        store_id: u64,
    ) -> Result<Option<metapb::Region>> {
        if let Some(first_region) = engines.kv.get_msg(keys::PREPARE_BOOTSTRAP_KEY)? {
            Ok(Some(first_region))
        } else {
            if self.check_cluster_bootstrapped()? {
                Ok(None)
            } else {
                self.prepare_bootstrap_cluster(engines, store_id).map(Some)
            }
        }
    }

    fn bootstrap_cluster(&mut self, engines: &Engines, first_region: metapb::Region) -> Result<()> {
        let region_id = first_region.get_id();
        let mut retry = 0;
        while retry < MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT {
            match self
                .pd_client
                .bootstrap_cluster(self.store.clone(), first_region.clone())
            {
                Ok(_) => {
                    info!("bootstrap cluster ok"; "cluster_id" => self.cluster_id);
                    fail_point!("node_after_bootstrap_cluster", |_| Err(box_err!(
                        "injected error: node_after_prepare_bootstrap_cluster"
                    )));
                    store::clear_prepare_bootstrap_key(engines)?;
                    return Ok(());
                }
                Err(PdError::ClusterBootstrapped(_)) => match self.pd_client.get_region(b"") {
                    Ok(region) => {
                        if region == first_region {
                            store::clear_prepare_bootstrap_key(engines)?;
                            return Ok(());
                        } else {
                            info!("cluster is already bootstrapped"; "cluster_id" => self.cluster_id);
                            store::clear_prepare_bootstrap_cluster(engines, region_id)?;
                            return Ok(());
                        }
                    }
                    Err(e) => {
                        warn!("get the first region failed"; "err" => ?e);
                    }
                },
                // TODO: should we clean region for other errors too?
                Err(e) => {
                    error!("bootstrap cluster"; "cluster_id" => self.cluster_id, "error" => ?e)
                }
            }
            retry += 1;
            thread::sleep(Duration::from_secs(
                CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS,
            ));
        }
        Err(box_err!("bootstrapped cluster failed"))
    }

    fn check_cluster_bootstrapped(&self) -> Result<bool> {
        for _ in 0..MAX_CHECK_CLUSTER_BOOTSTRAPPED_RETRY_COUNT {
            match self.pd_client.is_cluster_bootstrapped() {
                Ok(b) => return Ok(b),
                Err(e) => {
                    warn!("check cluster bootstrapped failed"; "err" => ?e);
                }
            }
            thread::sleep(Duration::from_secs(
                CHECK_CLUSTER_BOOTSTRAPPED_RETRY_SECONDS,
            ));
        }
        Err(box_err!("check cluster bootstrapped failed"))
    }

    #[allow(clippy::too_many_arguments)]
    fn start_store<T>(
        &mut self,
        store_id: u64,
        engines: Engines,
        trans: T,
        snap_mgr: SnapManager,
        pd_worker: FutureWorker<PdTask>,
        store_meta: Arc<Mutex<StoreMeta>>,
        coprocessor_host: CoprocessorHost,
        importer: Arc<SSTImporter>,
    ) -> Result<()>
    where
        T: Transport + 'static,
    {
        info!("start raft store thread"; "store_id" => store_id);

        if self.has_started {
            return Err(box_err!("{} is already started", store_id));
        }
        self.has_started = true;
        let cfg = self.store_cfg.clone();
        let pd_client = Arc::clone(&self.pd_client);
        let store = self.store.clone();
        self.system.spawn(
            store,
            cfg,
            engines,
            trans,
            pd_client,
            snap_mgr,
            pd_worker,
            store_meta,
            coprocessor_host,
            importer,
        )?;
        Ok(())
    }

    fn stop_store(&mut self, store_id: u64) {
        info!("stop raft store thread"; "store_id" => store_id);
        self.system.shutdown();
    }

    /// Stops the Node.
    pub fn stop(&mut self) {
        let store_id = self.store.get_id();
        self.stop_store(store_id)
    }
}
<|endoftext|>


<commit_before>//! Helper to crudely rewrite types from the spec's format to our own.
//!
//! This reads a file containing table and record descriptions (from the spec)
//! and converts them to the form that we use, writing the result to stdout.
//!
//! Input should be in the format:
//!
//!
//! ```
//! { // this is a 'group'
//! /// an optional comment for each top-level item
//! @table Gpos1_0
//! uint16      majorVersion       Major version of the GPOS table, = 1
//! uint16      minorVersion       Minor version of the GPOS table, = 0
//! Offset16    scriptListOffset   Offset to ScriptList table, from beginning of GPOS table
//! Offset16    featureListOffset  Offset to FeatureList table, from beginning of GPOS table
//! Offset16    lookupListOffset   Offset to LookupList table, from beginning of GPOS table
//!
//! @table Gpos1_1
//! uint16      majorVersion            Major version of the GPOS table, = 1
//! uint16      minorVersion            Minor version of the GPOS table, = 1
//! Offset16    scriptListOffset        Offset to ScriptList table, from beginning of GPOS table
//! Offset16    featureListOffset       Offset to FeatureList table, from beginning of GPOS table
//! Offset16    lookupListOffset        Offset to LookupList table, from beginning of GPOS table
//! Offset32    featureVariationsOffset Offset to FeatureVariations table, from beginning of GPOS table (may be NULL)
//! }
//! ```
//!
//! - different records/tables are separated by newlines.
//! - the first line should be a single word, used as the name of the type
//! - other lines are just copy pasted
//!
//! *limitations:* this doesn't handle lifetimes, and doesn't generate annotations.
//! You will need to clean up the output.

use std::{fmt::Write, ops::Deref};

macro_rules! exit_with_msg {
    ($disp:expr, $line:expr) => {{
        eprintln!("ERROR: {}", $disp);
        eprintln!("Line {}: '{}'", 1 + $line.number, $line.text);
        std::process::exit(1);
    }};
}

static MACRO_CALL: &str = "font_types::tables!";

/// a wrapper around a line, so we can report errors with line numbers
struct Line<'a> {
    text: &'a str,
    number: usize,
}

impl Deref for Line<'_> {
    type Target = str;
    fn deref(&self) -> &<Self as Deref>::Target {
        self.text
    }
}

fn main() {
    let in_path = std::env::args().nth(1).expect("expected path argument");
    let input = std::fs::read_to_string(in_path).expect("failed to read path");
    let mut lines = input
        .lines()
        .enumerate()
        .map(|(number, text)| Line {
            text: text.trim(),
            number,
        })
        .filter(|l| !l.starts_with('#'));

    while let Some(group) = generate_group(&mut lines) {
        println!("\n{} {{", MACRO_CALL);
        println!("{}", group);
        println!("}}");
    }
}

/// Generate a group of items. This is multiple items within a pair of brackets,
/// which will share a single macro invocation.
fn generate_group<'a>(lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let mut lines = lines.skip_while(|s| s.is_empty()).peekable();
    let brace = lines.next()?;

    let mut result = String::new();

    if !brace.starts_with('{') {
        exit_with_msg!("expected opening brace", brace);
    }

    let mut lines = lines.take_while(|line| !line.starts_with('}'));

    while let Some(item) = generate_one_item(&mut lines) {
        if !result.is_empty() {
            result.push_str("\n\n");
        }
        result.push_str(&item);
    }
    Some(result)
}

/// parse a single enum, table or record.
///
/// Returns `Some` on success, `None` if there are no more items, and terminates
/// if something goes wrong.
fn generate_one_item<'a>(lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let mut lines = lines.skip_while(|line| line.is_empty());
    let mut comments = Vec::new();
    let decl = loop {
        match lines.next() {
            Some(line) if line.starts_with("///") => comments.push(line.text),
            Some(line) if line.starts_with('@') => break Decl::parse(line).unwrap(),
            Some(line) => exit_with_msg!("expected table or record name", line),
            None => return None,
        }
    };

    let item = match decl.kind {
        DeclKind::RawEnum => generate_one_enum(decl, lines),
        DeclKind::Table | DeclKind::Record => generate_one_table(decl, lines),
    }?;
    let mut comments = comments.join("\n");
    if comments.is_empty() {
        Some(item)
    } else {
        comments.push('\n');
        comments.push_str(&item);
        Some(comments)
    }
}

/// Generate a single table or record (they're currently the same)
fn generate_one_table<'a>(decl: Decl, lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let fields = lines.map_while(parse_field).collect::<Vec<_>>();
    let lifetime_str = if fields.iter().any(|x| x.maybe_count.is_some()) {
        "<'a>"
    } else {
        ""
    };
    let mut result = String::new();
    writeln!(&mut result, "{}{} {{", decl.name, lifetime_str).unwrap();
    for line in &fields {
        writeln!(&mut result, "{}", line).unwrap();
    }
    result.push('}');
    Some(result)
}

fn generate_one_enum<'a>(decl: Decl, lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let fields = lines.map_while(parse_field).collect::<Vec<_>>();
    let mut result = String::new();
    writeln!(
        &mut result,
        "#[repr({})]\nenum {} {{",
        decl.annotation, decl.name
    )
    .unwrap();
    for line in &fields {
        writeln!(&mut result, "    {} = {},", line.name, line.typ).unwrap();
    }
    result.push('}');
    Some(result)
}

enum DeclKind {
    Table,
    Record,
    RawEnum,
}

struct Decl<'a> {
    kind: DeclKind,
    annotation: &'a str,
    name: &'a str,
}

impl<'a> Decl<'a> {
    fn parse(line: Line<'a>) -> Option<Self> {
        let mut decl = line.text.split_whitespace();
        let mut annotation = "";
        let kind = match decl.next()? {
            "@table" => DeclKind::Table,
            "@record" => DeclKind::Record,
            x if x.starts_with("@enum(") => {
                let repr = x.trim_start_matches("@enum(").trim_end_matches(')');
                if !["u8", "u16"].contains(&repr) {
                    exit_with_msg!(format!("unexpected enum repr '{}'", repr), line);
                }
                annotation = repr;
                DeclKind::RawEnum
            }
            other => exit_with_msg!(format!("unknown item kind '{}'", other), line),
        };
        let name = decl
            .next()
            .unwrap_or_else(|| exit_with_msg!("missing name", line));

        Some(Decl {
            kind,
            annotation,
            name,
        })
    }
}

struct Field<'a> {
    name: &'a str,
    maybe_count: Option<String>,
    typ: &'a str,
    comment: &'a str,
}

impl<'a> std::fmt::Display for Field<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        format_comment(f, "    ", self.comment)?;
        if self.name.contains("reserved") {
            writeln!(f, "    #[hidden]")?;
        }
        if let Some(count) = &self.maybe_count {
            writeln!(f, "    #[count({})]", count)?;
            write!(f, "    {}: [{}],", decamalize(self.name), self.typ)?;
        } else {
            write!(f, "    {}: {},", decamalize(self.name), self.typ)?;
        }
        Ok(())
    }
}

fn parse_field(line: Line) -> Option<Field> {
    if line.is_empty() {
        return None;
    }
    let mut iter = line.text.splitn(3, '\t');
    let (typ, ident, comment) = match (iter.next(), iter.next(), iter.next()) {
        (Some(a), Some(b), Some(c)) => (a, b, c),
        (Some(a), Some(b), None) => (a, b, ""),
        _ => exit_with_msg!("line could not be parsed as type/name/comment", line),
    };
    let typ = normalize_type(typ);
    let (name, maybe_count) = split_ident(ident);
    //let name = decamalize(name);
    let maybe_count = maybe_count.map(decamalize);
    Some(Field {
        name,
        maybe_count,
        typ,
        comment,
    })
}

/// takes an ident and splits it into the name and an optional count (if the item
/// is an array)
fn split_ident(input: &str) -> (&str, Option<&str>) {
    match input.split_once('[') {
        Some((front, back)) => (front, Some(back.trim_end_matches(']'))),
        None => (input, None),
    }
}

fn normalize_type(input: &str) -> &str {
    match input {
        "uint8" => "BigEndian<u8>",
        "uint16" => "BigEndian<u16>",
        "uint24" => "BigEndian<Uint24>",
        "uint32" => "BigEndian<u32>",
        "int8" => "BigEndian<i8>",
        "int16" => "BigEndian<i16>",
        "int32" => "BigEndian<i32>",
        "FWORD" => "BigEndian<FWord>",
        "UFWORD" => "BigEndian<UfWord>",
        "F2DOT14" => "BigEndian<F2Dot14>",
        "LONGDATETIME" => "BigEndian<LongDateTime>",
        "Version16Dot16" => "BigEndian<Version16Dot16>",
        "Fixed" => "BigEndian<Fixed>",
        "Tag" => "BigEndian<Tag>",
        "Offset16" => "BigEndian<Offset16>",
        "Offset24" => "BigEndian<Offset24>",
        "Offset32" => "BigEndian<Offset32>",
        other => other,
    }
}

fn decamalize(input: &str) -> String {
    //taken from serde: https://github.com/serde-rs/serde/blob/7e19ae8c9486a3bbbe51f1befb05edee94c454f9/serde_derive/src/internals/case.rs#L69-L76
    let mut snake = String::new();
    for (i, ch) in input.char_indices() {
        if i > 0 && ch.is_uppercase() {
            snake.push('_');
        }
        snake.push(ch.to_ascii_lowercase());
    }
    snake
}

fn format_comment(
    f: &mut std::fmt::Formatter<'_>,
    whitespace: &str,
    input: &str,
) -> std::fmt::Result {
    const LINE_LEN: usize = 72;

    let mut cur_len = 0;

    for token in input.split_inclusive(' ') {
        if cur_len == 0 || cur_len + token.len() > LINE_LEN {
            if cur_len > 0 {
                writeln!(f)?;
            }
            write!(f, "{}/// ", whitespace)?;
            cur_len = whitespace.len() + 4;
        }
        write!(f, "{}", token)?;
        cur_len += token.len();
    }
    if cur_len > 0 {
        writeln!(f)?;
    }
    Ok(())
}
<commit_msg>[codegen] generate bitflags<commit_after>//! Helper to crudely rewrite types from the spec's format to our own.
//!
//! This reads a file containing table and record descriptions (from the spec)
//! and converts them to the form that we use, writing the result to stdout.
//!
//! Input should be in the format:
//!
//!
//! ```
//! { // this is a 'group'
//! /// an optional comment for each top-level item
//! @table Gpos1_0
//! uint16      majorVersion       Major version of the GPOS table, = 1
//! uint16      minorVersion       Minor version of the GPOS table, = 0
//! Offset16    scriptListOffset   Offset to ScriptList table, from beginning of GPOS table
//! Offset16    featureListOffset  Offset to FeatureList table, from beginning of GPOS table
//! Offset16    lookupListOffset   Offset to LookupList table, from beginning of GPOS table
//!
//! @table Gpos1_1
//! uint16      majorVersion            Major version of the GPOS table, = 1
//! uint16      minorVersion            Minor version of the GPOS table, = 1
//! Offset16    scriptListOffset        Offset to ScriptList table, from beginning of GPOS table
//! Offset16    featureListOffset       Offset to FeatureList table, from beginning of GPOS table
//! Offset16    lookupListOffset        Offset to LookupList table, from beginning of GPOS table
//! Offset32    featureVariationsOffset Offset to FeatureVariations table, from beginning of GPOS table (may be NULL)
//! }
//! ```
//!
//! - different records/tables are separated by newlines.
//! - the first line should be a single word, used as the name of the type
//! - other lines are just copy pasted
//!
//! *limitations:* this doesn't handle lifetimes, and doesn't generate annotations.
//! You will need to clean up the output.

use std::{fmt::Write, ops::Deref};

macro_rules! exit_with_msg {
    ($disp:expr, $line:expr) => {{
        eprintln!("ERROR: {}", $disp);
        eprintln!("Line {}: '{}'", 1 + $line.number, $line.text);
        std::process::exit(1);
    }};
}

static MACRO_CALL: &str = "font_types::tables!";

/// a wrapper around a line, so we can report errors with line numbers
struct Line<'a> {
    text: &'a str,
    number: usize,
}

impl Deref for Line<'_> {
    type Target = str;
    fn deref(&self) -> &<Self as Deref>::Target {
        self.text
    }
}

fn main() {
    let in_path = std::env::args().nth(1).expect("expected path argument");
    let input = std::fs::read_to_string(in_path).expect("failed to read path");
    let mut lines = input
        .lines()
        .enumerate()
        .map(|(number, text)| Line {
            text: text.trim(),
            number,
        })
        .filter(|l| !l.starts_with('#'));

    while let Some(group) = generate_group(&mut lines) {
        println!("\n{} {{", MACRO_CALL);
        println!("{}", group);
        println!("}}");
    }
}

/// Generate a group of items. This is multiple items within a pair of brackets,
/// which will share a single macro invocation.
fn generate_group<'a>(lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let mut lines = lines.skip_while(|s| s.is_empty()).peekable();
    let brace = lines.next()?;

    let mut result = String::new();

    if !brace.starts_with('{') {
        exit_with_msg!("expected opening brace", brace);
    }

    let mut lines = lines.take_while(|line| !line.starts_with('}'));

    while let Some(item) = generate_one_item(&mut lines) {
        if !result.is_empty() {
            result.push_str("\n\n");
        }
        result.push_str(&item);
    }
    Some(result)
}

/// parse a single enum, table or record.
///
/// Returns `Some` on success, `None` if there are no more items, and terminates
/// if something goes wrong.
fn generate_one_item<'a>(lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let mut lines = lines.skip_while(|line| line.is_empty());
    let mut comments = Vec::new();
    let decl = loop {
        match lines.next() {
            Some(line) if line.starts_with("///") => comments.push(line.text),
            Some(line) if line.starts_with('@') => break Decl::parse(line).unwrap(),
            Some(line) => exit_with_msg!("expected table or record name", line),
            None => return None,
        }
    };

    let item = match decl.kind {
        DeclKind::RawEnum => generate_one_enum(decl, lines),
        DeclKind::Table | DeclKind::Record => generate_one_table(decl, lines),
        DeclKind::Flags => generate_one_flags(decl, lines),
    }?;
    let mut comments = comments.join("\n");
    if comments.is_empty() {
        Some(item)
    } else {
        comments.push('\n');
        comments.push_str(&item);
        Some(comments)
    }
}

/// Generate a single table or record (they're currently the same)
fn generate_one_table<'a>(decl: Decl, lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let fields = lines.map_while(parse_field).collect::<Vec<_>>();
    let lifetime_str = if fields.iter().any(|x| x.maybe_count.is_some()) {
        "<'a>"
    } else {
        ""
    };
    let mut result = String::new();
    writeln!(&mut result, "{}{} {{", decl.name, lifetime_str).unwrap();
    for line in &fields {
        writeln!(&mut result, "{}", line).unwrap();
    }
    result.push('}');
    Some(result)
}

fn generate_one_enum<'a>(decl: Decl, lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let fields = lines.map_while(parse_field).collect::<Vec<_>>();
    let mut result = String::new();
    writeln!(
        &mut result,
        "#[repr({})]\nenum {} {{",
        decl.annotation, decl.name
    )
    .unwrap();
    for line in &fields {
        writeln!(&mut result, "    {} = {},", line.name, line.typ).unwrap();
    }
    result.push('}');
    Some(result)
}

fn generate_one_flags<'a>(decl: Decl, lines: impl Iterator<Item = Line<'a>>) -> Option<String> {
    let fields = lines.map_while(parse_field).collect::<Vec<_>>();
    let mut result = String::new();
    writeln!(
        &mut result,
        "#[flags({})]\n{} {{",
        decl.annotation, decl.name
    )
    .unwrap();
    for line in &fields {
        format_comment(&mut result, "    ", line.comment).unwrap();
        writeln!(&mut result, "    {} = {},", line.name, line.typ).unwrap();
    }
    result.push('}');
    Some(result)
}

enum DeclKind {
    Table,
    Record,
    RawEnum,
    Flags,
}

struct Decl<'a> {
    kind: DeclKind,
    annotation: &'a str,
    name: &'a str,
}

impl<'a> Decl<'a> {
    fn parse(line: Line<'a>) -> Option<Self> {
        let mut decl = line.text.split_whitespace();
        let mut annotation = "";
        let kind = match decl.next()? {
            "@table" => DeclKind::Table,
            "@record" => DeclKind::Record,
            x if x.starts_with("@enum(") || x.starts_with("@flags(") => {
                let repr = x.split_once('(').unwrap().1.trim_end_matches(')');
                //let repr = x.trim_start_matches("@enum(").trim_end_matches(')');
                if !["u8", "u16"].contains(&repr) {
                    exit_with_msg!(format!("unexpected enum/flag repr '{}'", repr), line);
                }
                annotation = repr;
                if x.starts_with("@enum") {
                    DeclKind::RawEnum
                } else {
                    DeclKind::Flags
                }
            }
            other => exit_with_msg!(format!("unknown item kind '{}'", other), line),
        };
        let name = decl
            .next()
            .unwrap_or_else(|| exit_with_msg!("missing name", line));

        Some(Decl {
            kind,
            annotation,
            name,
        })
    }
}

struct Field<'a> {
    name: &'a str,
    maybe_count: Option<String>,
    typ: &'a str,
    comment: &'a str,
}

impl<'a> std::fmt::Display for Field<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        format_comment(f, "    ", self.comment)?;
        if self.name.contains("reserved") {
            writeln!(f, "    #[hidden]")?;
        }
        if let Some(count) = &self.maybe_count {
            writeln!(f, "    #[count({})]", count)?;
            write!(f, "    {}: [{}],", decamalize(self.name), self.typ)?;
        } else {
            write!(f, "    {}: {},", decamalize(self.name), self.typ)?;
        }
        Ok(())
    }
}

fn parse_field(line: Line) -> Option<Field> {
    if line.is_empty() {
        return None;
    }
    let mut iter = line.text.splitn(3, '\t');
    let (typ, ident, comment) = match (iter.next(), iter.next(), iter.next()) {
        (Some(a), Some(b), Some(c)) => (a, b, c),
        (Some(a), Some(b), None) => (a, b, ""),
        _ => exit_with_msg!("line could not be parsed as type/name/comment", line),
    };
    let typ = normalize_type(typ);
    let (name, maybe_count) = split_ident(ident);
    //let name = decamalize(name);
    let maybe_count = maybe_count.map(decamalize);
    Some(Field {
        name,
        maybe_count,
        typ,
        comment,
    })
}

/// takes an ident and splits it into the name and an optional count (if the item
/// is an array)
fn split_ident(input: &str) -> (&str, Option<&str>) {
    match input.split_once('[') {
        Some((front, back)) => (front, Some(back.trim_end_matches(']'))),
        None => (input, None),
    }
}

fn normalize_type(input: &str) -> &str {
    match input {
        "uint8" => "BigEndian<u8>",
        "uint16" => "BigEndian<u16>",
        "uint24" => "BigEndian<Uint24>",
        "uint32" => "BigEndian<u32>",
        "int8" => "BigEndian<i8>",
        "int16" => "BigEndian<i16>",
        "int32" => "BigEndian<i32>",
        "FWORD" => "BigEndian<FWord>",
        "UFWORD" => "BigEndian<UfWord>",
        "F2DOT14" => "BigEndian<F2Dot14>",
        "LONGDATETIME" => "BigEndian<LongDateTime>",
        "Version16Dot16" => "BigEndian<Version16Dot16>",
        "Fixed" => "BigEndian<Fixed>",
        "Tag" => "BigEndian<Tag>",
        "Offset16" => "BigEndian<Offset16>",
        "Offset24" => "BigEndian<Offset24>",
        "Offset32" => "BigEndian<Offset32>",
        other => other,
    }
}

fn decamalize(input: &str) -> String {
    //taken from serde: https://github.com/serde-rs/serde/blob/7e19ae8c9486a3bbbe51f1befb05edee94c454f9/serde_derive/src/internals/case.rs#L69-L76
    let mut snake = String::new();
    for (i, ch) in input.char_indices() {
        if i > 0 && ch.is_uppercase() {
            snake.push('_');
        }
        snake.push(ch.to_ascii_lowercase());
    }
    snake
}

fn format_comment(f: &mut dyn std::fmt::Write, whitespace: &str, input: &str) -> std::fmt::Result {
    const LINE_LEN: usize = 72;

    let mut cur_len = 0;

    for token in input.split_inclusive(' ') {
        if cur_len == 0 || cur_len + token.len() > LINE_LEN {
            if cur_len > 0 {
                writeln!(f)?;
            }
            write!(f, "{}/// ", whitespace)?;
            cur_len = whitespace.len() + 4;
        }
        write!(f, "{}", token)?;
        cur_len += token.len();
    }
    if cur_len > 0 {
        writeln!(f)?;
    }
    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>fix session doc test<commit_after><|endoftext|>


<commit_before>//! These macros are only intended to be used by inkwell internally
//! and should not be expected to have public support nor stability.
//! Here be dragons 🐉

extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::quote;
use syn::{Arm, Expr, ExprLit, Item, Lit, punctuated::Pair, Pat, PatLit, parse_macro_input};

// We could include_str! inkwell's Cargo.toml and extract these features
// so that they don't need to also be managed here...
const ALL_FEATURE_VERSIONS: [&str; 8] = [
    "llvm3-6",
    "llvm3-7",
    "llvm3-8",
    "llvm3-9",
    "llvm4-0",
    "llvm5-0",
    "llvm6-0",
    "llvm7-0",
];

fn panic_with_usage() -> ! {
    panic!("llvm_versions must take the form: X.Y => (X.Y || latest)");
}

fn get_latest_feature() -> String {
    ALL_FEATURE_VERSIONS[ALL_FEATURE_VERSIONS.len() - 1].into()
}

fn get_feature_slice(start_feature: &str, end_feature: &str) -> Option<&'static [&'static str]> {
    let start_index = ALL_FEATURE_VERSIONS.iter().position(|&str| str == start_feature)?;
    let end_index = ALL_FEATURE_VERSIONS.iter().position(|&str| str == end_feature)?;

    if end_index < start_index {
        return None;
    }

    Some(&ALL_FEATURE_VERSIONS[start_index..=end_index])
}

fn f64_to_feature_string(float: f64) -> String {
    let int = float as u64;

    format!("llvm{}-{}", int, (float * 10.) % 10.)
}

#[proc_macro_attribute]
pub fn llvm_versions(attribute_args: TokenStream, attributee: TokenStream) -> TokenStream {
    let arm = parse_macro_input!(attribute_args as Arm);

    if arm.pats.len() != 1 {
        panic_with_usage();
    }

    let start_feature = match arm.pats.first().unwrap() {
        Pair::End(Pat::Lit(PatLit { expr })) => match **expr {
            Expr::Lit(ExprLit { lit: Lit::Float(ref literal_float), .. }) => f64_to_feature_string(literal_float.value()),
            _ => panic_with_usage(),
        },
        _ => panic_with_usage(),
    };
    let end_feature = match *arm.body {
        Expr::Lit(ExprLit { lit: Lit::Float(literal_float), .. }) => Some(f64_to_feature_string(literal_float.value())),
        // We could assert the path is just "latest" but this seems like a lot of extra work...
        Expr::Path(_) => None,
        _ => panic_with_usage(),
    }.unwrap_or_else(get_latest_feature);

    let features = get_feature_slice(&start_feature, &end_feature).unwrap_or_else(|| panic_with_usage());
    let attributee: Item = syn::parse(attributee).expect("Could not parse attributed item");

    let q = quote! {
        #[cfg(any(#(feature = #features),*))]
        #attributee
    };

    q.into()
}
<commit_msg>Refactor llvm_versions macro<commit_after>//! These macros are only intended to be used by inkwell internally
//! and should not be expected to have public support nor stability.
//! Here be dragons 🐉

extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
use syn::parse::{Parse, ParseStream, Result, Error};
use syn::{Token, LitFloat, Ident, Item};

// We could include_str! inkwell's Cargo.toml and extract these features
// so that they don't need to also be managed here...
const ALL_FEATURE_VERSIONS: [&str; 9] = [
    "llvm3-6",
    "llvm3-7",
    "llvm3-8",
    "llvm3-9",
    "llvm4-0",
    "llvm5-0",
    "llvm6-0",
    "llvm7-0",
    "llvm8-0",
];

fn panic_with_usage() -> ! {
    panic!("llvm_versions must take the form: X.Y or X.Y => (X.Y || latest)");
}

fn get_latest_feature_index() -> usize {
    ALL_FEATURE_VERSIONS.len() - 1
}

fn get_feature_slice(vt: VersionType) -> Option<&'static [&'static str]> {
    match vt {
        VersionType::Specific(version) => {
            let feature = f64_to_feature_string(version);
            let index = ALL_FEATURE_VERSIONS.iter().position(|&s| s == feature)?;
            Some(&ALL_FEATURE_VERSIONS[index..index])
        }
        VersionType::RangeToLatest(version) => {
            let latest = get_latest_feature_index();
            let feature = f64_to_feature_string(version);
            let index = ALL_FEATURE_VERSIONS.iter().position(|&s| s == feature)?;
            Some(&ALL_FEATURE_VERSIONS[index..=latest])
        }
        VersionType::Range(start, end) => {
            let start_feature = f64_to_feature_string(start);
            let end_feature = f64_to_feature_string(end);
            let start_index = ALL_FEATURE_VERSIONS.iter().position(|&s| s == start_feature)?;
            let end_index = ALL_FEATURE_VERSIONS.iter().position(|&s| s == end_feature)?;
            if end_index < start_index {
                return None;
            }
            Some(&ALL_FEATURE_VERSIONS[start_index..=end_index])
        }
    }
}

fn f64_to_feature_string(float: f64) -> String {
    let int = float as u64;

    format!("llvm{}-{}", int, (float * 10.) % 10.)
}

enum VersionType {
    Specific(f64),
    RangeToLatest(f64),
    Range(f64, f64),
}
impl Parse for VersionType {
    fn parse(input: ParseStream) -> Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(LitFloat) {
            let from = input.parse::<LitFloat>().unwrap().value();
            if input.is_empty() {
                return Ok(VersionType::Specific(from));
            }
            let lookahead = input.lookahead1();
            if lookahead.peek(Token![=>]) {
                let _: Token![=>] = input.parse().unwrap();
                let lookahead = input.lookahead1();
                if lookahead.peek(Ident) {
                    let to = input.parse::<Ident>().unwrap();
                    if to.to_string() == "latest" {
                        Ok(VersionType::RangeToLatest(from))
                    } else {
                        Err(Error::new(to.span(), "expected `latest`"))
                    }
                } else if lookahead.peek(LitFloat) {
                    let to = input.parse::<LitFloat>().unwrap().value();
                    Ok(VersionType::Range(from, to))
                } else {
                    Err(lookahead.error())
                }
            } else {
                Err(lookahead.error())
            }
        } else {
            Err(lookahead.error())
        }
    }
}

#[proc_macro_attribute]
pub fn llvm_versions(attribute_args: TokenStream, attributee: TokenStream) -> TokenStream {
    let vt = parse_macro_input!(attribute_args as VersionType);

    let features = get_feature_slice(vt)
        .unwrap_or_else(|| panic_with_usage());

    let attributee: Item = syn::parse(attributee).expect("Could not parse attributed item");

    let q = quote! {
        #[cfg(any(#(feature = #features),*))]
        #attributee
    };

    q.into()
}
<|endoftext|>