<commit_before>// CITA
// Copyright 2016-2018 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use futures::future::Either;
use futures::sync::{mpsc, oneshot};
use futures::{Future, Sink, Stream};
use hyper;
use parking_lot::{Mutex, RwLock};
use serde_json;
use std::convert::TryInto;
use tokio_core::reactor::{Core, Timeout};

use cita_types::traits::LowerHex;
use cita_types::{H256, U256};
use configuration::UpStream;
use jsonrpc_types;
use libproto::blockchain::UnverifiedTransaction;

#[derive(Debug)]
pub enum Error {
    BadStatus,
    Timeout,
    Parse,
}

type RpcSender =
    Mutex<mpsc::Sender<(hyper::Request, oneshot::Sender<Result<hyper::Chunk, Error>>)>>;

pub struct RpcClient {
    sender: RpcSender,
    uri: RwLock<hyper::Uri>,
}

impl RpcClient {
    pub fn new(upstream: &UpStream) -> ::std::sync::Arc<Self> {
        let tb = ::std::thread::Builder::new().name("RpcClient".to_string());
        let uri = upstream.url.parse::<hyper::Uri>().unwrap();
        let (tx, rx) =
            mpsc::channel::<(hyper::Request, oneshot::Sender<Result<hyper::Chunk, Error>>)>(65_535);
        let timeout_duration = upstream.timeout;

        let _tb =
            tb.spawn(move || {
                let mut core = Core::new().unwrap();
                let handle = core.handle();
                let client = hyper::Client::configure()
                    .connector(hyper::client::HttpConnector::new(4, &handle))
                    .keep_alive(false)
                    .build(&handle);

                let messages = rx.for_each(|(req, sender)| {
                    let timeout = Timeout::new(timeout_duration, &handle).unwrap();
                    let post = client.request(req).and_then(|res| res.body().concat2());

                    let work = post.select2(timeout).then(move |res| match res {
                        Ok(Either::A((got, _timeout))) => {
                            let _ = sender.send(Ok(got));
                            Ok(())
                        }
                        Ok(Either::B((_timeout_error, _get))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                        Err(Either::A((_get_error, _timeout))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                        Err(Either::B((_timeout_error, _get))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                    });

                    handle.spawn(work);
                    Ok(())
                });

                core.run(messages).unwrap();
            }).expect("Couldn't spawn a thread.");

        ::std::sync::Arc::new(RpcClient {
            sender: Mutex::new(tx),
            uri: RwLock::new(uri),
        })
    }

    pub fn do_post(&self, body: &str) -> Result<hyper::Chunk, Error> {
        let uri = { self.uri.read().clone() };
        trace!("Send body {:?} to {:?}.", body, uri);
        let mut req = hyper::Request::new(hyper::Method::Post, uri);
        req.headers_mut().set(hyper::header::ContentType::json());
        req.set_body(body.to_owned());
        let (tx, rx) = oneshot::channel();
        {
            let _ = self.sender.lock().start_send((req, tx));
        }
        match rx.wait() {
            Ok(res) => {
                let res = res.map_err(|_| Error::BadStatus)?;
                trace!("Get response {:?}.", res);
                Ok(res)
            }
            Err(_) => Err(Error::BadStatus),
        }
    }
}

// Pack the result type into a reply type, and parse result from the reply, and return the result.
// The user of this macro do NOT have to care about the inner reply type.
macro_rules! rpc_send_and_get_result_from_reply {
    ($upstream:ident, $method:expr, $params:tt, $result_type:path) => {{
        define_reply_type!(ReplyType, $result_type);
        let rpc_cli = RpcClient::new($upstream);
        let body: String = json!({
                    "jsonrpc": "2.0",
                    "method": $method,
                    "params": json!($params),
                    "id": 1
                }).to_string();
        let data = rpc_cli.do_post(&body)?;
        let reply: ReplyType = serde_json::from_slice(&data).map_err(|_| {
            error!(
                "send {:?} return error: {:?}",
                &body,
                ::std::str::from_utf8(&data)
            );
            Error::Parse
        })?;
        trace!("get reply {:?}.", reply);
        reply.result
    }};
}

macro_rules! define_reply_type {
    ($reply_type:ident, $result_type:path) => {
        #[derive(Debug, Clone, Serialize, Deserialize)]
        struct $reply_type {
            pub jsonrpc: Option<jsonrpc_types::Version>,
            pub id: jsonrpc_types::Id,
            pub result: $result_type,
        }
    };
}

pub fn cita_get_transaction_proof(upstream: &UpStream, tx_hash: H256) -> Result<Vec<u8>, Error> {
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_getTransactionProof",
        [tx_hash],
        jsonrpc_types::bytes::Bytes
    );
    Ok(result.vec())
}

pub fn cita_block_number(upstream: &UpStream) -> Result<U256, Error> {
    let result = rpc_send_and_get_result_from_reply!(upstream, "cita_blockNumber", [], U256);
    Ok(result)
}

pub fn cita_get_metadata(upstream: &UpStream) -> Result<jsonrpc_types::rpctypes::MetaData, Error> {
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_getMetaData",
        ["latest"],
        jsonrpc_types::rpctypes::MetaData
    );
    Ok(result)
}

pub fn cita_send_transaction(
    upstream: &UpStream,
    utx: &UnverifiedTransaction,
) -> Result<H256, Error> {
    let tx_bytes: Vec<u8> = utx.try_into().unwrap();
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_sendTransaction",
        [tx_bytes.lower_hex()],
        jsonrpc_types::rpctypes::TxResponse
    );
    if result.status.to_uppercase() == "OK" {
        Ok(result.hash)
    } else {
        Err(Error::BadStatus)
    }
}
<commit_msg>eliminate error caused by cargo fmt<commit_after>// CITA
// Copyright 2016-2018 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use futures::future::Either;
use futures::sync::{mpsc, oneshot};
use futures::{Future, Sink, Stream};
use hyper;
use parking_lot::{Mutex, RwLock};
use serde_json;
use std::convert::TryInto;
use tokio_core::reactor::{Core, Timeout};

use cita_types::traits::LowerHex;
use cita_types::{H256, U256};
use configuration::UpStream;
use jsonrpc_types;
use libproto::blockchain::UnverifiedTransaction;

#[derive(Debug)]
pub enum Error {
    BadStatus,
    Timeout,
    Parse,
}

type RpcSender =
    Mutex<mpsc::Sender<(hyper::Request, oneshot::Sender<Result<hyper::Chunk, Error>>)>>;

pub struct RpcClient {
    sender: RpcSender,
    uri: RwLock<hyper::Uri>,
}

impl RpcClient {
    pub fn new(upstream: &UpStream) -> ::std::sync::Arc<Self> {
        let tb = ::std::thread::Builder::new().name("RpcClient".to_string());
        let uri = upstream.url.parse::<hyper::Uri>().unwrap();
        let (tx, rx) =
            mpsc::channel::<(hyper::Request, oneshot::Sender<Result<hyper::Chunk, Error>>)>(65_535);
        let timeout_duration = upstream.timeout;

        let _tb =
            tb.spawn(move || {
                let mut core = Core::new().unwrap();
                let handle = core.handle();
                let client = hyper::Client::configure()
                    .connector(hyper::client::HttpConnector::new(4, &handle))
                    .keep_alive(false)
                    .build(&handle);

                let messages = rx.for_each(|(req, sender)| {
                    let timeout = Timeout::new(timeout_duration, &handle).unwrap();
                    let post = client.request(req).and_then(|res| res.body().concat2());

                    let work = post.select2(timeout).then(move |res| match res {
                        Ok(Either::A((got, _timeout))) => {
                            let _ = sender.send(Ok(got));
                            Ok(())
                        }
                        Ok(Either::B((_timeout_error, _get))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                        Err(Either::A((_get_error, _timeout))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                        Err(Either::B((_timeout_error, _get))) => {
                            let _ = sender.send(Err(Error::Timeout));
                            Ok(())
                        }
                    });

                    handle.spawn(work);
                    Ok(())
                });

                core.run(messages).unwrap();
            }).expect("Couldn't spawn a thread.");

        ::std::sync::Arc::new(RpcClient {
            sender: Mutex::new(tx),
            uri: RwLock::new(uri),
        })
    }

    pub fn do_post(&self, body: &str) -> Result<hyper::Chunk, Error> {
        let uri = { self.uri.read().clone() };
        trace!("Send body {:?} to {:?}.", body, uri);
        let mut req = hyper::Request::new(hyper::Method::Post, uri);
        req.headers_mut().set(hyper::header::ContentType::json());
        req.set_body(body.to_owned());
        let (tx, rx) = oneshot::channel();
        {
            let _ = self.sender.lock().start_send((req, tx));
        }
        match rx.wait() {
            Ok(res) => {
                let res = res.map_err(|_| Error::BadStatus)?;
                trace!("Get response {:?}.", res);
                Ok(res)
            }
            Err(_) => Err(Error::BadStatus),
        }
    }
}

// Pack the result type into a reply type, and parse result from the reply, and return the result.
// The user of this macro do NOT have to care about the inner reply type.
macro_rules! rpc_send_and_get_result_from_reply {
    ($upstream:ident, $method:expr, $params:tt, $result_type:path) => {{
        define_reply_type!(ReplyType, $result_type);
        let rpc_cli = RpcClient::new($upstream);
        let body: String = json!({"jsonrpc": "2.0", "method": $method, "params": json!($params), "id": 1}).to_string();
        let data = rpc_cli.do_post(&body)?;
        let reply: ReplyType = serde_json::from_slice(&data).map_err(|_| {
            error!(
                "send {:?} return error: {:?}",
                &body,
                ::std::str::from_utf8(&data)
            );
            Error::Parse
        })?;
        trace!("get reply {:?}.", reply);
        reply.result
    }};
}

macro_rules! define_reply_type {
    ($reply_type:ident, $result_type:path) => {
        #[derive(Debug, Clone, Serialize, Deserialize)]
        struct $reply_type {
            pub jsonrpc: Option<jsonrpc_types::Version>,
            pub id: jsonrpc_types::Id,
            pub result: $result_type,
        }
    };
}

pub fn cita_get_transaction_proof(upstream: &UpStream, tx_hash: H256) -> Result<Vec<u8>, Error> {
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_getTransactionProof",
        [tx_hash],
        jsonrpc_types::bytes::Bytes
    );
    Ok(result.vec())
}

pub fn cita_block_number(upstream: &UpStream) -> Result<U256, Error> {
    let result = rpc_send_and_get_result_from_reply!(upstream, "cita_blockNumber", [], U256);
    Ok(result)
}

pub fn cita_get_metadata(upstream: &UpStream) -> Result<jsonrpc_types::rpctypes::MetaData, Error> {
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_getMetaData",
        ["latest"],
        jsonrpc_types::rpctypes::MetaData
    );
    Ok(result)
}

pub fn cita_send_transaction(
    upstream: &UpStream,
    utx: &UnverifiedTransaction,
) -> Result<H256, Error> {
    let tx_bytes: Vec<u8> = utx.try_into().unwrap();
    let result = rpc_send_and_get_result_from_reply!(
        upstream,
        "cita_sendTransaction",
        [tx_bytes.lower_hex()],
        jsonrpc_types::rpctypes::TxResponse
    );
    if result.status.to_uppercase() == "OK" {
        Ok(result.hash)
    } else {
        Err(Error::BadStatus)
    }
}
<|endoftext|>


<commit_before>//! This module add real world mbe example for benchmark tests

use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, NameOwner},
    AstNode, SmolStr,
};
use test_utils::{bench, bench_fixture, skip_slow_tests};

use crate::{
    ast_to_token_tree,
    parser::{Op, RepeatKind, Separator},
    MacroRules,
};

#[test]
fn benchmark_parse_macro_rules() {
    if skip_slow_tests() {
        return;
    }
    let rules = macro_rules_fixtures_tt();
    let hash: usize = {
        let _pt = bench("mbe parse macro rules");
        rules.values().map(|it| MacroRules::parse(it).unwrap().rules.len()).sum()
    };
    assert_eq!(hash, 1144);
}

#[test]
fn benchmark_expand_macro_rules() {
    if skip_slow_tests() {
        return;
    }
    let rules = macro_rules_fixtures();
    let invocations = invocation_fixtures(&rules);

    let hash: usize = {
        let _pt = bench("mbe expand macro rules");
        invocations
            .into_iter()
            .map(|(id, tt)| {
                let res = rules[&id].expand(&tt);
                if res.err.is_some() {
                    // FIXME:
                    // Currently `invocation_fixtures` will generate some correct invocations but
                    // cannot be expanded by mbe. We ignore errors here.
                    // See: https://github.com/rust-analyzer/rust-analyzer/issues/4777
                    eprintln!("err from {} {:?}", id, res.err);
                }
                res.value.token_trees.len()
            })
            .sum()
    };
    assert_eq!(hash, 66995);
}

fn macro_rules_fixtures() -> FxHashMap<String, MacroRules> {
    macro_rules_fixtures_tt()
        .into_iter()
        .map(|(id, tt)| (id, MacroRules::parse(&tt).unwrap()))
        .collect()
}

fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree> {
    let fixture = bench_fixture::numerous_macro_rules();
    let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();

    source_file
        .syntax()
        .descendants()
        .filter_map(ast::MacroRules::cast)
        .map(|rule| {
            let id = rule.name().unwrap().to_string();
            let (def_tt, _) = ast_to_token_tree(&rule.token_tree().unwrap()).unwrap();
            (id, def_tt)
        })
        .collect()
}

// Generate random invocation fixtures from rules
fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt::Subtree)> {
    let mut seed = 123456789;
    let mut res = Vec::new();

    for (name, it) in rules {
        for rule in &it.rules {
            // Generate twice
            for _ in 0..2 {
                let mut subtree = tt::Subtree::default();
                for op in rule.lhs.iter() {
                    collect_from_op(op, &mut subtree, &mut seed);
                }
                res.push((name.clone(), subtree));
            }
        }
    }
    return res;

    fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) {
        return match op {
            Op::Var { kind, .. } => match kind.as_ref().map(|it| it.as_str()) {
                Some("ident") => parent.token_trees.push(make_ident("foo")),
                Some("ty") => parent.token_trees.push(make_ident("Foo")),
                Some("tt") => parent.token_trees.push(make_ident("foo")),
                Some("vis") => parent.token_trees.push(make_ident("pub")),
                Some("pat") => parent.token_trees.push(make_ident("foo")),
                Some("path") => parent.token_trees.push(make_ident("foo")),
                Some("literal") => parent.token_trees.push(make_literal("1")),
                Some("expr") => parent.token_trees.push(make_ident("foo").into()),
                Some("lifetime") => {
                    parent.token_trees.push(make_punct('\''));
                    parent.token_trees.push(make_ident("a"));
                }
                Some("block") => {
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
                }
                Some("item") => {
                    parent.token_trees.push(make_ident("fn"));
                    parent.token_trees.push(make_ident("foo"));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
                }
                Some("meta") => {
                    parent.token_trees.push(make_ident("foo"));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
                }

                None => (),
                Some(kind) => panic!("Unhandled kind {}", kind),
            },
            Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()),
            Op::Repeat { tokens, kind, separator } => {
                let max = 10;
                let cnt = match kind {
                    RepeatKind::ZeroOrMore => rand(seed) % max,
                    RepeatKind::OneOrMore => 1 + rand(seed) % max,
                    RepeatKind::ZeroOrOne => rand(seed) % 2,
                };
                for i in 0..cnt {
                    for it in tokens.iter() {
                        collect_from_op(it, parent, seed);
                    }
                    if i + 1 != cnt {
                        if let Some(sep) = separator {
                            match sep {
                                Separator::Literal(it) => parent
                                    .token_trees
                                    .push(tt::Leaf::Literal(it.clone().into()).into()),
                                Separator::Ident(it) => parent
                                    .token_trees
                                    .push(tt::Leaf::Ident(it.clone().into()).into()),
                                Separator::Puncts(puncts) => {
                                    for it in puncts {
                                        parent
                                            .token_trees
                                            .push(tt::Leaf::Punct(it.clone().into()).into())
                                    }
                                }
                            };
                        }
                    }
                }
            }
            Op::Subtree { tokens, delimiter } => {
                let mut subtree =
                    tt::Subtree { delimiter: delimiter.clone(), token_trees: Vec::new() };
                tokens.iter().for_each(|it| {
                    collect_from_op(it, &mut subtree, seed);
                });
                parent.token_trees.push(subtree.into());
            }
        };

        // Simple linear congruential generator for determistic result
        fn rand(seed: &mut usize) -> usize {
            let a = 1664525;
            let c = 1013904223;
            *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
            return *seed;
        };
        fn make_ident(ident: &str) -> tt::TokenTree {
            tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) })
                .into()
        }
        fn make_punct(char: char) -> tt::TokenTree {
            tt::Leaf::Punct(tt::Punct {
                id: tt::TokenId::unspecified(),
                char,
                spacing: tt::Spacing::Alone,
            })
            .into()
        }
        fn make_literal(lit: &str) -> tt::TokenTree {
            tt::Leaf::Literal(tt::Literal {
                id: tt::TokenId::unspecified(),
                text: SmolStr::new(lit),
            })
            .into()
        }
        fn make_subtree(
            kind: tt::DelimiterKind,
            token_trees: Option<Vec<tt::TokenTree>>,
        ) -> tt::TokenTree {
            tt::Subtree {
                delimiter: Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind }),
                token_trees: token_trees.unwrap_or_default(),
            }
            .into()
        }
    }
}
<commit_msg>Remove redundant semicolon<commit_after>//! This module add real world mbe example for benchmark tests

use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, NameOwner},
    AstNode, SmolStr,
};
use test_utils::{bench, bench_fixture, skip_slow_tests};

use crate::{
    ast_to_token_tree,
    parser::{Op, RepeatKind, Separator},
    MacroRules,
};

#[test]
fn benchmark_parse_macro_rules() {
    if skip_slow_tests() {
        return;
    }
    let rules = macro_rules_fixtures_tt();
    let hash: usize = {
        let _pt = bench("mbe parse macro rules");
        rules.values().map(|it| MacroRules::parse(it).unwrap().rules.len()).sum()
    };
    assert_eq!(hash, 1144);
}

#[test]
fn benchmark_expand_macro_rules() {
    if skip_slow_tests() {
        return;
    }
    let rules = macro_rules_fixtures();
    let invocations = invocation_fixtures(&rules);

    let hash: usize = {
        let _pt = bench("mbe expand macro rules");
        invocations
            .into_iter()
            .map(|(id, tt)| {
                let res = rules[&id].expand(&tt);
                if res.err.is_some() {
                    // FIXME:
                    // Currently `invocation_fixtures` will generate some correct invocations but
                    // cannot be expanded by mbe. We ignore errors here.
                    // See: https://github.com/rust-analyzer/rust-analyzer/issues/4777
                    eprintln!("err from {} {:?}", id, res.err);
                }
                res.value.token_trees.len()
            })
            .sum()
    };
    assert_eq!(hash, 66995);
}

fn macro_rules_fixtures() -> FxHashMap<String, MacroRules> {
    macro_rules_fixtures_tt()
        .into_iter()
        .map(|(id, tt)| (id, MacroRules::parse(&tt).unwrap()))
        .collect()
}

fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree> {
    let fixture = bench_fixture::numerous_macro_rules();
    let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();

    source_file
        .syntax()
        .descendants()
        .filter_map(ast::MacroRules::cast)
        .map(|rule| {
            let id = rule.name().unwrap().to_string();
            let (def_tt, _) = ast_to_token_tree(&rule.token_tree().unwrap()).unwrap();
            (id, def_tt)
        })
        .collect()
}

// Generate random invocation fixtures from rules
fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt::Subtree)> {
    let mut seed = 123456789;
    let mut res = Vec::new();

    for (name, it) in rules {
        for rule in &it.rules {
            // Generate twice
            for _ in 0..2 {
                let mut subtree = tt::Subtree::default();
                for op in rule.lhs.iter() {
                    collect_from_op(op, &mut subtree, &mut seed);
                }
                res.push((name.clone(), subtree));
            }
        }
    }
    return res;

    fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) {
        return match op {
            Op::Var { kind, .. } => match kind.as_ref().map(|it| it.as_str()) {
                Some("ident") => parent.token_trees.push(make_ident("foo")),
                Some("ty") => parent.token_trees.push(make_ident("Foo")),
                Some("tt") => parent.token_trees.push(make_ident("foo")),
                Some("vis") => parent.token_trees.push(make_ident("pub")),
                Some("pat") => parent.token_trees.push(make_ident("foo")),
                Some("path") => parent.token_trees.push(make_ident("foo")),
                Some("literal") => parent.token_trees.push(make_literal("1")),
                Some("expr") => parent.token_trees.push(make_ident("foo").into()),
                Some("lifetime") => {
                    parent.token_trees.push(make_punct('\''));
                    parent.token_trees.push(make_ident("a"));
                }
                Some("block") => {
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
                }
                Some("item") => {
                    parent.token_trees.push(make_ident("fn"));
                    parent.token_trees.push(make_ident("foo"));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
                }
                Some("meta") => {
                    parent.token_trees.push(make_ident("foo"));
                    parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
                }

                None => (),
                Some(kind) => panic!("Unhandled kind {}", kind),
            },
            Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()),
            Op::Repeat { tokens, kind, separator } => {
                let max = 10;
                let cnt = match kind {
                    RepeatKind::ZeroOrMore => rand(seed) % max,
                    RepeatKind::OneOrMore => 1 + rand(seed) % max,
                    RepeatKind::ZeroOrOne => rand(seed) % 2,
                };
                for i in 0..cnt {
                    for it in tokens.iter() {
                        collect_from_op(it, parent, seed);
                    }
                    if i + 1 != cnt {
                        if let Some(sep) = separator {
                            match sep {
                                Separator::Literal(it) => parent
                                    .token_trees
                                    .push(tt::Leaf::Literal(it.clone().into()).into()),
                                Separator::Ident(it) => parent
                                    .token_trees
                                    .push(tt::Leaf::Ident(it.clone().into()).into()),
                                Separator::Puncts(puncts) => {
                                    for it in puncts {
                                        parent
                                            .token_trees
                                            .push(tt::Leaf::Punct(it.clone().into()).into())
                                    }
                                }
                            };
                        }
                    }
                }
            }
            Op::Subtree { tokens, delimiter } => {
                let mut subtree =
                    tt::Subtree { delimiter: delimiter.clone(), token_trees: Vec::new() };
                tokens.iter().for_each(|it| {
                    collect_from_op(it, &mut subtree, seed);
                });
                parent.token_trees.push(subtree.into());
            }
        };

        // Simple linear congruential generator for determistic result
        fn rand(seed: &mut usize) -> usize {
            let a = 1664525;
            let c = 1013904223;
            *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
            return *seed;
        }
        fn make_ident(ident: &str) -> tt::TokenTree {
            tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) })
                .into()
        }
        fn make_punct(char: char) -> tt::TokenTree {
            tt::Leaf::Punct(tt::Punct {
                id: tt::TokenId::unspecified(),
                char,
                spacing: tt::Spacing::Alone,
            })
            .into()
        }
        fn make_literal(lit: &str) -> tt::TokenTree {
            tt::Leaf::Literal(tt::Literal {
                id: tt::TokenId::unspecified(),
                text: SmolStr::new(lit),
            })
            .into()
        }
        fn make_subtree(
            kind: tt::DelimiterKind,
            token_trees: Option<Vec<tt::TokenTree>>,
        ) -> tt::TokenTree {
            tt::Subtree {
                delimiter: Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind }),
                token_trees: token_trees.unwrap_or_default(),
            }
            .into()
        }
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg, SubCommand};
use core::str;
use ra_tools::{
    gen_tests, generate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, Cmd,
    Overwrite, Result,
};
use std::{env, path::PathBuf};

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(
            SubCommand::with_name("install-ra")
                .arg(Arg::with_name("server").long("--server"))
                .arg(Arg::with_name("jemalloc").long("jemalloc").requires("server"))
                .arg(Arg::with_name("client-code").long("client-code").conflicts_with("server")),
        )
        .alias("install-code")
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .subcommand(SubCommand::with_name("fuzz-tests"))
        .subcommand(SubCommand::with_name("lint"))
        .get_matches();
    match matches.subcommand() {
        ("install-ra", Some(matches)) => {
            let opts = InstallOpt {
                client: if matches.is_present("server") { None } else { Some(ClientOpt::VsCode) },
                server: if matches.is_present("client-code") {
                    None
                } else {
                    Some(ServerOpt { jemalloc: matches.is_present("jemalloc") })
                },
            };
            install(opts)?
        }
        ("gen-tests", _) => gen_tests(Overwrite)?,
        ("gen-syntax", _) => generate(Overwrite)?,
        ("format", _) => run_rustfmt(Overwrite)?,
        ("format-hook", _) => install_format_hook()?,
        ("lint", _) => run_clippy()?,
        ("fuzz-tests", _) => run_fuzzer()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;

    let code_in_path = Cmd {
        unix: r"code --version",
        windows: r"cmd.exe /c code.cmd --version",
        work_dir: "./editors/code",
    }
    .run()
    .is_ok();
    if !code_in_path {
        Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?;
    }

    Cmd {
        unix: r"code --install-extension ./ra-lsp-0.0.1.vsix --force",
        windows: r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: r"code --list-extensions",
        windows: r"cmd.exe /c code.cmd --list-extensions",
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --force", ".")
    }
}
<commit_msg>it's a good idea to build extension before installing it<commit_after>use clap::{App, Arg, SubCommand};
use core::str;
use ra_tools::{
    gen_tests, generate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt, Cmd,
    Overwrite, Result,
};
use std::{env, path::PathBuf};

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(
            SubCommand::with_name("install-ra")
                .arg(Arg::with_name("server").long("--server"))
                .arg(Arg::with_name("jemalloc").long("jemalloc"))
                .arg(Arg::with_name("client-code").long("client-code").conflicts_with("server")),
        )
        .alias("install-code")
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .subcommand(SubCommand::with_name("fuzz-tests"))
        .subcommand(SubCommand::with_name("lint"))
        .get_matches();
    match matches.subcommand() {
        ("install-ra", Some(matches)) => {
            let opts = InstallOpt {
                client: if matches.is_present("server") { None } else { Some(ClientOpt::VsCode) },
                server: if matches.is_present("client-code") {
                    None
                } else {
                    Some(ServerOpt { jemalloc: matches.is_present("jemalloc") })
                },
            };
            install(opts)?
        }
        ("gen-tests", _) => gen_tests(Overwrite)?,
        ("gen-syntax", _) => generate(Overwrite)?,
        ("format", _) => run_rustfmt(Overwrite)?,
        ("format-hook", _) => install_format_hook()?,
        ("lint", _) => run_clippy()?,
        ("fuzz-tests", _) => run_fuzzer()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;
    Cmd {
        unix: r"npm run package",
        windows: r"cmd.exe /c npm.cmd run package",
        work_dir: "./editors/code",
    }
    .run()?;

    let code_in_path = Cmd {
        unix: r"code --version",
        windows: r"cmd.exe /c code.cmd --version",
        work_dir: "./editors/code",
    }
    .run()
    .is_ok();
    if !code_in_path {
        Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?;
    }

    Cmd {
        unix: r"code --install-extension ./ra-lsp-0.0.1.vsix --force",
        windows: r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: r"code --list-extensions",
        windows: r"cmd.exe /c code.cmd --list-extensions",
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --force", ".")
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg, SubCommand};
use core::str;
use ra_tools::{
    gen_tests, generate_boilerplate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt,
    Cmd, Overwrite, Result,
};
use std::{env, path::PathBuf};

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(
            SubCommand::with_name("install-ra")
                .arg(Arg::with_name("server").long("--server"))
                .arg(Arg::with_name("jemalloc").long("jemalloc"))
                .arg(Arg::with_name("client-code").long("client-code").conflicts_with("server")),
        )
        .alias("install-code")
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .subcommand(SubCommand::with_name("fuzz-tests"))
        .subcommand(SubCommand::with_name("lint"))
        .get_matches();
    match matches.subcommand() {
        ("install-ra", Some(matches)) => {
            let opts = InstallOpt {
                client: if matches.is_present("server") { None } else { Some(ClientOpt::VsCode) },
                server: if matches.is_present("client-code") {
                    None
                } else {
                    Some(ServerOpt { jemalloc: matches.is_present("jemalloc") })
                },
            };
            install(opts)?
        }
        ("gen-tests", _) => gen_tests(Overwrite)?,
        ("gen-syntax", _) => generate_boilerplate(Overwrite)?,
        ("format", _) => run_rustfmt(Overwrite)?,
        ("format-hook", _) => install_format_hook()?,
        ("lint", _) => run_clippy()?,
        ("fuzz-tests", _) => run_fuzzer()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;
    Cmd {
        unix: r"npm run package",
        windows: r"cmd.exe /c npm.cmd run package",
        work_dir: "./editors/code",
    }
    .run()?;

    let code_in_path = Cmd {
        unix: r"code --version",
        windows: r"cmd.exe /c code.cmd --version",
        work_dir: "./editors/code",
    }
    .run()
    .is_ok();
    if !code_in_path {
        Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?;
    }

    Cmd {
        unix: r"code --install-extension ./ra-lsp-0.0.1.vsix --force",
        windows: r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: r"code --list-extensions",
        windows: r"cmd.exe /c code.cmd --list-extensions",
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --force", ".")
    }
}
<commit_msg>make sure lockfile is used for cargo install<commit_after>use clap::{App, Arg, SubCommand};
use core::str;
use ra_tools::{
    gen_tests, generate_boilerplate, install_format_hook, run, run_clippy, run_fuzzer, run_rustfmt,
    Cmd, Overwrite, Result,
};
use std::{env, path::PathBuf};

struct InstallOpt {
    client: Option<ClientOpt>,
    server: Option<ServerOpt>,
}

enum ClientOpt {
    VsCode,
}

struct ServerOpt {
    jemalloc: bool,
}

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(
            SubCommand::with_name("install-ra")
                .arg(Arg::with_name("server").long("--server"))
                .arg(Arg::with_name("jemalloc").long("jemalloc"))
                .arg(Arg::with_name("client-code").long("client-code").conflicts_with("server")),
        )
        .alias("install-code")
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .subcommand(SubCommand::with_name("fuzz-tests"))
        .subcommand(SubCommand::with_name("lint"))
        .get_matches();
    match matches.subcommand() {
        ("install-ra", Some(matches)) => {
            let opts = InstallOpt {
                client: if matches.is_present("server") { None } else { Some(ClientOpt::VsCode) },
                server: if matches.is_present("client-code") {
                    None
                } else {
                    Some(ServerOpt { jemalloc: matches.is_present("jemalloc") })
                },
            };
            install(opts)?
        }
        ("gen-tests", _) => gen_tests(Overwrite)?,
        ("gen-syntax", _) => generate_boilerplate(Overwrite)?,
        ("format", _) => run_rustfmt(Overwrite)?,
        ("format-hook", _) => install_format_hook()?,
        ("lint", _) => run_clippy()?,
        ("fuzz-tests", _) => run_fuzzer()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn install(opts: InstallOpt) -> Result<()> {
    if cfg!(target_os = "macos") {
        fix_path_for_mac()?
    }
    if let Some(server) = opts.server {
        install_server(server)?;
    }
    if let Some(client) = opts.client {
        install_client(client)?;
    }
    Ok(())
}

fn fix_path_for_mac() -> Result<()> {
    let mut vscode_path: Vec<PathBuf> = {
        const COMMON_APP_PATH: &str =
            r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
        const ROOT_DIR: &str = "";
        let home_dir = match env::var("HOME") {
            Ok(home) => home,
            Err(e) => Err(format!("Failed getting HOME from environment with error: {}.", e))?,
        };

        [ROOT_DIR, &home_dir]
            .iter()
            .map(|dir| String::from(*dir) + COMMON_APP_PATH)
            .map(PathBuf::from)
            .filter(|path| path.exists())
            .collect()
    };

    if !vscode_path.is_empty() {
        let vars = match env::var_os("PATH") {
            Some(path) => path,
            None => Err("Could not get PATH variable from env.")?,
        };

        let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
        paths.append(&mut vscode_path);
        let new_paths = env::join_paths(paths)?;
        env::set_var("PATH", &new_paths);
    }

    Ok(())
}

fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
    Cmd { unix: r"npm ci", windows: r"cmd.exe /c npm.cmd ci", work_dir: "./editors/code" }.run()?;
    Cmd {
        unix: r"npm run package",
        windows: r"cmd.exe /c npm.cmd run package",
        work_dir: "./editors/code",
    }
    .run()?;

    let code_in_path = Cmd {
        unix: r"code --version",
        windows: r"cmd.exe /c code.cmd --version",
        work_dir: "./editors/code",
    }
    .run()
    .is_ok();
    if !code_in_path {
        Err("Can't execute `code --version`. Perhaps it is not in $PATH?")?;
    }

    Cmd {
        unix: r"code --install-extension ./ra-lsp-0.0.1.vsix --force",
        windows: r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
        work_dir: "./editors/code",
    }
    .run()?;

    let output = Cmd {
        unix: r"code --list-extensions",
        windows: r"cmd.exe /c code.cmd --list-extensions",
        work_dir: ".",
    }
    .run_with_output()?;

    if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
        Err("Could not install the Visual Studio Code extension. \
             Please make sure you have at least NodeJS 10.x installed and try again.")?;
    }

    Ok(())
}

fn install_server(opts: ServerOpt) -> Result<()> {
    if opts.jemalloc {
        run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".")
    } else {
        run("cargo install --path crates/ra_lsp_server --locked --force", ".")
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;
use std::str::FromStr;

use clap::ArgMatches;
use etcd::Client;

use decryption::Decryptor;

pub struct Agent {
    decryptor: Decryptor,
    etcd: Client,
    role: Role,
}

#[derive(Clone, Copy)]
enum Role {
    Master,
    Node,
}

impl Agent {
    pub fn new<'a>(matches: &'a ArgMatches) -> Result<Self, String> {
        Ok(Agent {
            decryptor: try!(Decryptor::new(
                matches.value_of("region").expect("clap should have required region")
            )),
            etcd: Client::new(&["http://127.0.0.1:2379"]).expect("Failed to create etcd client"),
            role: matches.value_of("role").expect("clap should have required role").parse().expect(
              "clap should have required a valid value for role"
            )
        })
    }

    pub fn run(mut self) -> Result<Option<String>, String> {
        println!("kaws-agent is starting...");
        println!("Checking current modifiedIndex of /kaws key...");
        let mut modified_index = match self.etcd.get("/kaws", false, false, false) {
            Ok(key_space_info) => match key_space_info.node.unwrap().modified_index {
                Some(new_index) => new_index,
                None => return Err("WARNING: etcd node for /kaws had no modified index".to_owned()),
            },
            Err(errors) => return Err(errors[0].to_string()),
        };

        if let Err(error) = self.refresh() {
            println!("ERROR: {}", error);
        }

        loop {
            println!("Watching /kaws key...");
            match self.etcd.watch("/kaws", Some(modified_index), true) {
                Ok(key_space_info) => {
                    if let Err(error) = self.refresh() {
                        println!("WARNING: {}", error);
                    }

                    match key_space_info.node.unwrap().modified_index {
                        Some(new_index) => modified_index = new_index,
                        None => println!("WARNING: etcd node for watched key had no modified index"),
                    }
                }
                Err(errors) => println!("ERROR: {}", errors[0]),
            }
        }
    }

    // private

    fn refresh(&mut self) -> Result<Option<String>, String> {
        println!("Refreshing files from data in etcd...");
        try!(self.refresh_item("/kaws/pki/ca.pem", "/etc/kubernetes/ssl/ca.pem", false));

        match &self.role {
            &Role::Master => {
                try!(self.refresh_item(
                    "/kaws/pki/master.pem",
                    "/etc/kubernetes/ssl/master.pem",
                    false,
                ));
                try!(self.refresh_item(
                    "/kaws/pki/master-key.pem",
                    "/etc/kubernetes/ssl/master-key.pem",
                    true,
                ));
            }
            &Role::Node => {
                try!(self.refresh_item(
                    "/kaws/pki/node.pem",
                    "/etc/kubernetes/ssl/node.pem",
                    false,
                ));
                try!(self.refresh_item(
                    "/kaws/pki/node-key.pem",
                    "/etc/kubernetes/ssl/node-key.pem",
                    true,
                ));
            }
        }

        Ok(None)
    }

    fn refresh_item(&mut self, key: &str, file_path: &str, encrypted: bool)
    -> Result<Option<String>, String> {
        println!("Refreshing file {} from etcd key {}", file_path, key);
        let key_space_info = match self.etcd.get(key, false, false, false) {
            Ok(key_space_info) => key_space_info,
            Err(errors) => return Err(errors[0].to_string()),
        };

        let value = match key_space_info.node.unwrap().value {
            Some(value) => if encrypted {
                match self.decryptor.decrypt(&value) {
                    Ok(plaintext) => plaintext,
                    Err(error) => return Err(error),
                }
            } else {
                value
            },
            None => return Err(format!("etcd key {} had no value", key)),
        };

        let mut file = match File::create(file_path) {
            Ok(file) => file,
            Err(error) => return Err(error.to_string()),
        };

        match file.write_all(value.as_bytes()) {
            Ok(_) => Ok(None),
            Err(error) => Err(error.to_string()),
        }
    }
}

impl FromStr for Role {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "master" => Ok(Role::Master),
            "node" => Ok(Role::Node),
            role => Err(format!("Unknown server role for agent: {}", role)),
        }
    }
}
<commit_msg>Use file name for all etcd key names and restart kubelet after a refresh.<commit_after>use std::fs::File;
use std::io::Write;
use std::process::Command;
use std::str::FromStr;

use clap::ArgMatches;
use etcd::Client;

use decryption::Decryptor;

pub struct Agent {
    decryptor: Decryptor,
    etcd: Client,
    role: Role,
}

#[derive(Clone, Copy)]
enum Role {
    Master,
    Node,
}

impl Agent {
    pub fn new<'a>(matches: &'a ArgMatches) -> Result<Self, String> {
        Ok(Agent {
            decryptor: try!(Decryptor::new(
                matches.value_of("region").expect("clap should have required region")
            )),
            etcd: Client::new(&["http://127.0.0.1:2379"]).expect("Failed to create etcd client"),
            role: matches.value_of("role").expect("clap should have required role").parse().expect(
              "clap should have required a valid value for role"
            )
        })
    }

    pub fn run(mut self) -> Result<Option<String>, String> {
        println!("kaws-agent is starting...");
        println!("Checking current modifiedIndex of /kaws key...");
        let mut modified_index = match self.etcd.get("/kaws", false, false, false) {
            Ok(key_space_info) => match key_space_info.node.unwrap().modified_index {
                Some(new_index) => new_index,
                None => return Err("WARNING: etcd node for /kaws had no modified index".to_owned()),
            },
            Err(errors) => return Err(errors[0].to_string()),
        };

        if let Err(error) = self.refresh() {
            println!("ERROR: {}", error);
        }

        loop {
            println!("Watching /kaws key...");
            match self.etcd.watch("/kaws", Some(modified_index), true) {
                Ok(key_space_info) => {
                    if let Err(error) = self.refresh() {
                        println!("WARNING: {}", error);
                    }

                    match key_space_info.node.unwrap().modified_index {
                        Some(new_index) => modified_index = new_index,
                        None => println!("WARNING: etcd node for watched key had no modified index"),
                    }
                }
                Err(errors) => println!("ERROR: {}", errors[0]),
            }
        }
    }

    // private

    fn refresh(&mut self) -> Result<Option<String>, String> {
        println!("Refreshing files from data in etcd...");
        try!(self.refresh_item("/kaws/pki/ca.pem", "/etc/kubernetes/ssl/ca.pem", false));

        match &self.role {
            &Role::Master => {
                try!(self.refresh_item(
                    "/kaws/pki/master.pem",
                    "/etc/kubernetes/ssl/master.pem",
                    false,
                ));
                try!(self.refresh_item(
                    "/kaws/pki/master-key-encrypted.base64",
                    "/etc/kubernetes/ssl/master-key.pem",
                    true,
                ));
            }
            &Role::Node => {
                try!(self.refresh_item(
                    "/kaws/pki/node.pem",
                    "/etc/kubernetes/ssl/node.pem",
                    false,
                ));
                try!(self.refresh_item(
                    "/kaws/pki/node-key-encrypted.base64",
                    "/etc/kubernetes/ssl/node-key.pem",
                    true,
                ));
            }
        }

        try!(self.restart_kubelet());

        Ok(None)
    }

    fn refresh_item(&mut self, key: &str, file_path: &str, encrypted: bool)
    -> Result<Option<String>, String> {
        println!("Refreshing file {} from etcd key {}", file_path, key);
        let key_space_info = match self.etcd.get(key, false, false, false) {
            Ok(key_space_info) => key_space_info,
            Err(errors) => return Err(errors[0].to_string()),
        };

        let value = match key_space_info.node.unwrap().value {
            Some(value) => if encrypted {
                match self.decryptor.decrypt(&value) {
                    Ok(plaintext) => plaintext,
                    Err(error) => return Err(error),
                }
            } else {
                value
            },
            None => return Err(format!("etcd key {} had no value", key)),
        };

        let mut file = match File::create(file_path) {
            Ok(file) => file,
            Err(error) => return Err(error.to_string()),
        };

        match file.write_all(value.as_bytes()) {
            Ok(_) => Ok(None),
            Err(error) => Err(error.to_string()),
        }
    }

    fn restart_kubelet(&self) -> Result<Option<String>, String> {
        match Command::new("sudo").args(&["systemctl", "restart", "kubelet"]).status() {
            Ok(status) => {
                if status.success() {
                    return Ok(None)
                } else if let Some(code) = status.code() {
                    return Err(format!("Failed to restart kubelet: exit code {}", code));
                } else {
                    return Err("Failed to restart kubelet: non-zero exit code.".to_owned());
                }
            }
            Err(error) => return Err(format!("Failed to restart kubelet: {}", error)),
        }
    }
}

impl FromStr for Role {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "master" => Ok(Role::Master),
            "node" => Ok(Role::Node),
            role => Err(format!("Unknown server role for agent: {}", role)),
        }
    }
}
<|endoftext|>


<commit_before>use codec::{eyre::Result, stencila_schema::*, EncodeOptions};
use itertools::Itertools;
use std::cmp::max;

/// Encode a `Node` to Markdown
pub fn encode(node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
    Ok(node.to_md().trim().to_string())
}

/// A trait to encode a `Node` as Markdown
pub trait ToMd {
    fn to_md(&self) -> String;
}

macro_rules! slice_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                self.iter()
                    .map(|item| item.to_md())
                    .collect::<Vec<String>>()
                    .concat()
            }
        }
    };
}
slice_to_md!([Node]);
slice_to_md!([InlineContent]);
slice_to_md!([BlockContent]);

macro_rules! delimited_inline_content_to_md {
    ($type:ty, $delimiter:expr) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                [$delimiter, &self.content.to_md(), $delimiter].concat()
            }
        }
    };
}

delimited_inline_content_to_md!(Delete, "~~");
delimited_inline_content_to_md!(Emphasis, "_");
delimited_inline_content_to_md!(Strong, "**");
delimited_inline_content_to_md!(Subscript, "~");
delimited_inline_content_to_md!(Superscript, "^");

impl ToMd for NontextualAnnotation {
    fn to_md(&self) -> String {
        ["", &self.content.to_md(), ""].concat()
    }
}

impl ToMd for Quote {
    fn to_md(&self) -> String {
        ["<q>", &self.content.to_md(), "</q>"].concat()
    }
}

impl ToMd for CodeExpression {
    fn to_md(&self) -> String {
        ["`", &self.text, "`{", &self.programming_language, " exec}"].concat()
    }
}

macro_rules! delimited_inline_text_to_md {
    ($type:ty, $delimiter:expr) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                [$delimiter, &self.text, $delimiter].concat()
            }
        }
    };
}

delimited_inline_text_to_md!(CodeFragment, "`");
delimited_inline_text_to_md!(MathFragment, "$");

impl ToMd for Link {
    fn to_md(&self) -> String {
        ["[", &self.content.to_md(), "](", &self.target, ")"].concat()
    }
}

macro_rules! inline_media_object_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                ["![", "](", &self.content_url, ")"].concat()
            }
        }
    };
}

inline_media_object_to_md!(AudioObjectSimple);
inline_media_object_to_md!(ImageObjectSimple);
inline_media_object_to_md!(VideoObjectSimple);

impl ToMd for Heading {
    fn to_md(&self) -> String {
        [
            &"#".repeat(self.depth.unwrap_or(1) as usize),
            " ",
            &self.content.to_md(),
            "\n\n",
        ]
        .concat()
    }
}

impl ToMd for Paragraph {
    fn to_md(&self) -> String {
        [&self.content.to_md(), "\n\n"].concat()
    }
}

impl ToMd for CodeBlock {
    fn to_md(&self) -> String {
        let lang = match &self.programming_language {
            Some(boxed) => boxed.as_str(),
            None => "",
        };

        ["```", lang, "\n", &self.text, "\n```\n\n"].concat()
    }
}

impl ToMd for CodeChunk {
    fn to_md(&self) -> String {
        [
            "```",
            &self.programming_language,
            " exec \n",
            &self.text,
            "\n```\n\n",
        ]
        .concat()
    }
}

impl ToMd for List {
    fn to_md(&self) -> String {
        let ordered = matches!(&self.order, Some(ListOrder::Ascending));
        let items: Vec<String> = self
            .items
            .iter()
            .enumerate()
            .map(|(index, item)| {
                let bullet = if ordered {
                    (index + 1).to_string() + ". "
                } else {
                    "- ".to_string()
                };
                item.to_md()
                    .split('\n')
                    .enumerate()
                    .map(|(index, line)| {
                        if index == 0 {
                            [bullet.clone(), line.to_string()].concat()
                        } else if line.trim().is_empty() {
                            String::new()
                        } else {
                            ["  ", line].concat()
                        }
                    })
                    .join("\n")
            })
            .collect();

        // Keep lists tight if no items have internal newlines
        let mut tight = true;
        for item in &items {
            if item.trim().contains('\n') {
                tight = false;
                break;
            }
        }
        let items = items
            .iter()
            .map(|item| item.trim())
            .join(if tight { "\n" } else { "\n\n" });

        [items, "\n\n".to_string()].concat()
    }
}

impl ToMd for ListItem {
    fn to_md(&self) -> String {
        let checkbox = self.is_checked.map(|is_checked| match is_checked {
            true => InlineContent::String("[x] ".to_string()),
            false => InlineContent::String("[ ] ".to_string()),
        });
        match &self.content {
            Some(content) => match content {
                ListItemContent::VecInlineContent(inlines) => match checkbox {
                    Some(checkbox) => [vec![checkbox], inlines.clone()].concat().to_md(),
                    None => inlines.to_md(),
                },
                ListItemContent::VecBlockContent(blocks) => match checkbox {
                    Some(checkbox) => {
                        // Check box is only added is the first block is a paragraph
                        if let Some(BlockContent::Paragraph(paragraph)) = blocks.first() {
                            let mut paragraph = paragraph.clone();
                            paragraph.content.insert(0, checkbox);
                            [paragraph.to_md(), blocks[1..].to_md()].concat()
                        } else {
                            blocks.to_md()
                        }
                    }
                    None => blocks.to_md(),
                },
            },
            None => "".to_string(),
        }
    }
}

impl ToMd for QuoteBlock {
    fn to_md(&self) -> String {
        let content: Vec<String> = self
            .content
            .iter()
            .map(|block| {
                block
                    .to_md()
                    .split('\n')
                    .map(|line| ["> ", line].concat())
                    .join("\n")
            })
            .collect();
        [content.join("\n"), "\n\n".to_string()].concat()
    }
}

impl ToMd for TableSimple {
    fn to_md(&self) -> String {
        let mut column_widths: Vec<usize> = Vec::new();
        let mut rows: Vec<Vec<String>> = Vec::new();
        for row in &self.rows {
            let mut cells: Vec<String> = Vec::new();
            for (column, cell) in row.cells.iter().enumerate() {
                let content = match &cell.content {
                    None => "".to_string(),
                    Some(content) => match content {
                        TableCellContent::VecInlineContent(inlines) => inlines.to_md(),
                        TableCellContent::VecBlockContent(blocks) => blocks.to_md(),
                    },
                };
                let width = content.len();
                match column_widths.get_mut(column) {
                    Some(column_width) => {
                        if width > *column_width {
                            *column_width = width
                        }
                    }
                    None => column_widths.push(max(3, width)),
                }
                cells.push(content);
            }
            rows.push(cells);
        }

        let row_to_md = |cells: &[String]| -> String {
            cells
                .iter()
                .enumerate()
                .map(|(column, content)| {
                    format!("{:width$}", content, width = column_widths[column])
                })
                .join(" | ")
        };

        let (first, rest) = if rows.len() == 1 {
            (
                row_to_md(&vec!["".to_string(); column_widths.len()]),
                row_to_md(&rows[0]),
            )
        } else {
            (
                row_to_md(&rows[0]),
                rows[1..].iter().map(|row| row_to_md(row)).join(" |\n| "),
            )
        };

        let dashes = column_widths
            .iter()
            .map(|width| "-".repeat(*width))
            .join(" | ");

        [
            "| ", &first, " |\n", "| ", &dashes, " |\n", "| ", &rest, " |\n\n",
        ]
        .concat()
    }
}

impl ToMd for ThematicBreak {
    fn to_md(&self) -> String {
        "---\n\n".to_string()
    }
}

impl ToMd for Article {
    fn to_md(&self) -> String {
        match &self.content {
            Some(content) => content.to_md(),
            None => "".to_string(),
        }
    }
}

/// Encode a `Node` to plain text
impl ToMd for Node {
    fn to_md(&self) -> String {
        match self {
            Node::Article(node) => node.to_md(),
            Node::Boolean(node) => node.to_string(),
            //Node::Cite(node) => node.to_md(),
            Node::CodeBlock(node) => node.to_md(),
            Node::CodeFragment(node) => node.to_md(),
            Node::Delete(node) => node.to_md(),
            Node::Emphasis(node) => node.to_md(),
            Node::Heading(node) => node.to_md(),
            Node::Integer(node) => node.to_string(),
            Node::Link(node) => node.to_md(),
            Node::List(node) => node.to_md(),
            Node::NontextualAnnotation(node) => node.to_md(),
            //Node::Note(node) => node.to_md(),
            Node::Null(node) => node.to_string(),
            Node::Number(node) => node.to_string(),
            Node::Paragraph(node) => node.to_md(),
            Node::Quote(node) => node.to_md(),
            Node::QuoteBlock(node) => node.to_md(),
            Node::String(node) => node.to_string(),
            Node::Strong(node) => node.to_md(),
            Node::Subscript(node) => node.to_md(),
            Node::Superscript(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for InlineContent {
    fn to_md(&self) -> String {
        match self {
            InlineContent::AudioObject(node) => node.to_md(),
            InlineContent::Boolean(node) => node.to_string(),
            //InlineContent::Cite(node) => node.to_md(),
            InlineContent::CodeExpression(node) => node.to_md(),
            InlineContent::CodeFragment(node) => node.to_md(),
            InlineContent::Delete(node) => node.to_md(),
            InlineContent::Emphasis(node) => node.to_md(),
            InlineContent::ImageObject(node) => node.to_md(),
            InlineContent::Integer(node) => node.to_string(),
            InlineContent::Link(node) => node.to_md(),
            InlineContent::NontextualAnnotation(node) => node.to_md(),
            //InlineContent::Note(node) => node.to_md(),
            InlineContent::Null(node) => node.to_string(),
            InlineContent::Number(node) => node.to_string(),
            InlineContent::MathFragment(node) => node.to_md(),
            InlineContent::Quote(node) => node.to_md(),
            InlineContent::String(node) => node.to_string(),
            InlineContent::Strong(node) => node.to_md(),
            InlineContent::Subscript(node) => node.to_md(),
            InlineContent::Superscript(node) => node.to_md(),
            InlineContent::VideoObject(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for BlockContent {
    fn to_md(&self) -> String {
        match self {
            //BlockContent::Claim(node) => node.to_md(),
            BlockContent::CodeBlock(node) => node.to_md(),
            BlockContent::CodeChunk(node) => node.to_md(),
            BlockContent::Heading(node) => node.to_md(),
            BlockContent::List(node) => node.to_md(),
            //BlockContent::MathBlock(node) => node.to_md(),
            BlockContent::Paragraph(node) => node.to_md(),
            BlockContent::QuoteBlock(node) => node.to_md(),
            BlockContent::Table(node) => node.to_md(),
            BlockContent::ThematicBreak(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for ThingDescription {
    fn to_md(&self) -> String {
        match self {
            ThingDescription::String(string) => string.to_string(),
            ThingDescription::VecInlineContent(inlines) => inlines.to_md(),
            ThingDescription::VecBlockContent(blocks) => blocks.to_md(),
        }
    }
}
<commit_msg>fix(Markdown encoding): Add support for encoding `CreativeWork`<commit_after>use codec::{eyre::Result, stencila_schema::*, EncodeOptions};
use itertools::Itertools;
use std::cmp::max;

/// Encode a `Node` to Markdown
pub fn encode(node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
    Ok(node.to_md().trim().to_string())
}

/// A trait to encode a `Node` as Markdown
pub trait ToMd {
    fn to_md(&self) -> String;
}

macro_rules! primitive_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                self.to_string()
            }
        }
    };
}

primitive_to_md!(Null);
primitive_to_md!(Boolean);
primitive_to_md!(Integer);
primitive_to_md!(Number);
primitive_to_md!(String);

impl<Type> ToMd for Option<Type>
where
    Type: ToMd,
{
    fn to_md(&self) -> String {
        match self {
            Some(value) => value.to_md(),
            None => "".to_string(),
        }
    }
}

impl<Type> ToMd for Box<Type>
where
    Type: ToMd,
{
    fn to_md(&self) -> String {
        self.as_ref().to_md()
    }
}

impl<Type> ToMd for Vec<Type>
where
    Type: ToMd,
{
    fn to_md(&self) -> String {
        self.iter()
            .map(|item| item.to_md())
            .collect::<Vec<String>>()
            .concat()
    }
}

macro_rules! slice_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                self.iter()
                    .map(|item| item.to_md())
                    .collect::<Vec<String>>()
                    .concat()
            }
        }
    };
}
slice_to_md!([Node]);
slice_to_md!([InlineContent]);
slice_to_md!([BlockContent]);

macro_rules! delimited_inline_content_to_md {
    ($type:ty, $delimiter:expr) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                [$delimiter, &self.content.to_md(), $delimiter].concat()
            }
        }
    };
}

delimited_inline_content_to_md!(Delete, "~~");
delimited_inline_content_to_md!(Emphasis, "_");
delimited_inline_content_to_md!(Strong, "**");
delimited_inline_content_to_md!(Subscript, "~");
delimited_inline_content_to_md!(Superscript, "^");

impl ToMd for NontextualAnnotation {
    fn to_md(&self) -> String {
        ["", &self.content.to_md(), ""].concat()
    }
}

impl ToMd for Quote {
    fn to_md(&self) -> String {
        ["<q>", &self.content.to_md(), "</q>"].concat()
    }
}

impl ToMd for CodeExpression {
    fn to_md(&self) -> String {
        ["`", &self.text, "`{", &self.programming_language, " exec}"].concat()
    }
}

macro_rules! delimited_inline_text_to_md {
    ($type:ty, $delimiter:expr) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                [$delimiter, &self.text, $delimiter].concat()
            }
        }
    };
}

delimited_inline_text_to_md!(CodeFragment, "`");
delimited_inline_text_to_md!(MathFragment, "$");

impl ToMd for Link {
    fn to_md(&self) -> String {
        ["[", &self.content.to_md(), "](", &self.target, ")"].concat()
    }
}

macro_rules! inline_media_object_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                ["![", "](", &self.content_url, ")"].concat()
            }
        }
    };
}

inline_media_object_to_md!(AudioObjectSimple);
inline_media_object_to_md!(ImageObjectSimple);
inline_media_object_to_md!(VideoObjectSimple);

impl ToMd for Heading {
    fn to_md(&self) -> String {
        [
            &"#".repeat(self.depth.unwrap_or(1) as usize),
            " ",
            &self.content.to_md(),
            "\n\n",
        ]
        .concat()
    }
}

impl ToMd for Paragraph {
    fn to_md(&self) -> String {
        [&self.content.to_md(), "\n\n"].concat()
    }
}

impl ToMd for CodeBlock {
    fn to_md(&self) -> String {
        let lang = match &self.programming_language {
            Some(boxed) => boxed.as_str(),
            None => "",
        };

        ["```", lang, "\n", &self.text, "\n```\n\n"].concat()
    }
}

impl ToMd for CodeChunk {
    fn to_md(&self) -> String {
        [
            "```",
            &self.programming_language,
            " exec \n",
            &self.text,
            "\n```\n\n",
        ]
        .concat()
    }
}

impl ToMd for List {
    fn to_md(&self) -> String {
        let ordered = matches!(&self.order, Some(ListOrder::Ascending));
        let items: Vec<String> = self
            .items
            .iter()
            .enumerate()
            .map(|(index, item)| {
                let bullet = if ordered {
                    (index + 1).to_string() + ". "
                } else {
                    "- ".to_string()
                };
                item.to_md()
                    .split('\n')
                    .enumerate()
                    .map(|(index, line)| {
                        if index == 0 {
                            [bullet.clone(), line.to_string()].concat()
                        } else if line.trim().is_empty() {
                            String::new()
                        } else {
                            ["  ", line].concat()
                        }
                    })
                    .join("\n")
            })
            .collect();

        // Keep lists tight if no items have internal newlines
        let mut tight = true;
        for item in &items {
            if item.trim().contains('\n') {
                tight = false;
                break;
            }
        }
        let items = items
            .iter()
            .map(|item| item.trim())
            .join(if tight { "\n" } else { "\n\n" });

        [items, "\n\n".to_string()].concat()
    }
}

impl ToMd for ListItem {
    fn to_md(&self) -> String {
        let checkbox = self.is_checked.map(|is_checked| match is_checked {
            true => InlineContent::String("[x] ".to_string()),
            false => InlineContent::String("[ ] ".to_string()),
        });
        match &self.content {
            Some(content) => match content {
                ListItemContent::VecInlineContent(inlines) => match checkbox {
                    Some(checkbox) => [vec![checkbox], inlines.clone()].concat().to_md(),
                    None => inlines.to_md(),
                },
                ListItemContent::VecBlockContent(blocks) => match checkbox {
                    Some(checkbox) => {
                        // Check box is only added is the first block is a paragraph
                        if let Some(BlockContent::Paragraph(paragraph)) = blocks.first() {
                            let mut paragraph = paragraph.clone();
                            paragraph.content.insert(0, checkbox);
                            [paragraph.to_md(), blocks[1..].to_md()].concat()
                        } else {
                            blocks.to_md()
                        }
                    }
                    None => blocks.to_md(),
                },
            },
            None => "".to_string(),
        }
    }
}

impl ToMd for QuoteBlock {
    fn to_md(&self) -> String {
        let content: Vec<String> = self
            .content
            .iter()
            .map(|block| {
                block
                    .to_md()
                    .split('\n')
                    .map(|line| ["> ", line].concat())
                    .join("\n")
            })
            .collect();
        [content.join("\n"), "\n\n".to_string()].concat()
    }
}

impl ToMd for TableSimple {
    fn to_md(&self) -> String {
        let mut column_widths: Vec<usize> = Vec::new();
        let mut rows: Vec<Vec<String>> = Vec::new();
        for row in &self.rows {
            let mut cells: Vec<String> = Vec::new();
            for (column, cell) in row.cells.iter().enumerate() {
                let content = match &cell.content {
                    None => "".to_string(),
                    Some(content) => match content {
                        TableCellContent::VecInlineContent(inlines) => inlines.to_md(),
                        TableCellContent::VecBlockContent(blocks) => blocks.to_md(),
                    },
                };
                let width = content.len();
                match column_widths.get_mut(column) {
                    Some(column_width) => {
                        if width > *column_width {
                            *column_width = width
                        }
                    }
                    None => column_widths.push(max(3, width)),
                }
                cells.push(content);
            }
            rows.push(cells);
        }

        let row_to_md = |cells: &[String]| -> String {
            cells
                .iter()
                .enumerate()
                .map(|(column, content)| {
                    format!("{:width$}", content, width = column_widths[column])
                })
                .join(" | ")
        };

        let (first, rest) = if rows.len() == 1 {
            (
                row_to_md(&vec!["".to_string(); column_widths.len()]),
                row_to_md(&rows[0]),
            )
        } else {
            (
                row_to_md(&rows[0]),
                rows[1..].iter().map(|row| row_to_md(row)).join(" |\n| "),
            )
        };

        let dashes = column_widths
            .iter()
            .map(|width| "-".repeat(*width))
            .join(" | ");

        [
            "| ", &first, " |\n", "| ", &dashes, " |\n", "| ", &rest, " |\n\n",
        ]
        .concat()
    }
}

impl ToMd for ThematicBreak {
    fn to_md(&self) -> String {
        "---\n\n".to_string()
    }
}

macro_rules! content_to_md {
    ($type:ty) => {
        impl ToMd for $type {
            fn to_md(&self) -> String {
                self.content.to_md()
            }
        }
    };
}

content_to_md!(Article);
content_to_md!(CreativeWork);

impl ToMd for CreativeWorkContent {
    fn to_md(&self) -> String {
        match self {
            CreativeWorkContent::String(node) => node.to_md(),
            CreativeWorkContent::VecNode(nodes) => nodes.to_md(),
        }
    }
}

impl ToMd for Node {
    fn to_md(&self) -> String {
        match self {
            Node::Article(node) => node.to_md(),
            Node::Boolean(node) => node.to_md(),
            //Node::Cite(node) => node.to_md(),
            Node::CodeBlock(node) => node.to_md(),
            Node::CodeFragment(node) => node.to_md(),
            Node::CreativeWork(node) => node.to_md(),
            Node::Delete(node) => node.to_md(),
            Node::Emphasis(node) => node.to_md(),
            Node::Heading(node) => node.to_md(),
            Node::Integer(node) => node.to_md(),
            Node::Link(node) => node.to_md(),
            Node::List(node) => node.to_md(),
            Node::NontextualAnnotation(node) => node.to_md(),
            //Node::Note(node) => node.to_md(),
            Node::Null(node) => node.to_md(),
            Node::Number(node) => node.to_md(),
            Node::Paragraph(node) => node.to_md(),
            Node::Quote(node) => node.to_md(),
            Node::QuoteBlock(node) => node.to_md(),
            Node::String(node) => node.to_md(),
            Node::Strong(node) => node.to_md(),
            Node::Subscript(node) => node.to_md(),
            Node::Superscript(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for InlineContent {
    fn to_md(&self) -> String {
        match self {
            InlineContent::AudioObject(node) => node.to_md(),
            InlineContent::Boolean(node) => node.to_md(),
            //InlineContent::Cite(node) => node.to_md(),
            InlineContent::CodeExpression(node) => node.to_md(),
            InlineContent::CodeFragment(node) => node.to_md(),
            InlineContent::Delete(node) => node.to_md(),
            InlineContent::Emphasis(node) => node.to_md(),
            InlineContent::ImageObject(node) => node.to_md(),
            InlineContent::Integer(node) => node.to_md(),
            InlineContent::Link(node) => node.to_md(),
            InlineContent::NontextualAnnotation(node) => node.to_md(),
            //InlineContent::Note(node) => node.to_md(),
            InlineContent::Null(node) => node.to_md(),
            InlineContent::Number(node) => node.to_md(),
            InlineContent::MathFragment(node) => node.to_md(),
            InlineContent::Quote(node) => node.to_md(),
            InlineContent::String(node) => node.to_md(),
            InlineContent::Strong(node) => node.to_md(),
            InlineContent::Subscript(node) => node.to_md(),
            InlineContent::Superscript(node) => node.to_md(),
            InlineContent::VideoObject(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for BlockContent {
    fn to_md(&self) -> String {
        match self {
            //BlockContent::Claim(node) => node.to_md(),
            BlockContent::CodeBlock(node) => node.to_md(),
            BlockContent::CodeChunk(node) => node.to_md(),
            BlockContent::Heading(node) => node.to_md(),
            BlockContent::List(node) => node.to_md(),
            //BlockContent::MathBlock(node) => node.to_md(),
            BlockContent::Paragraph(node) => node.to_md(),
            BlockContent::QuoteBlock(node) => node.to_md(),
            BlockContent::Table(node) => node.to_md(),
            BlockContent::ThematicBreak(node) => node.to_md(),
            _ => "".to_string(),
        }
    }
}

impl ToMd for ThingDescription {
    fn to_md(&self) -> String {
        match self {
            ThingDescription::String(string) => string.to_string(),
            ThingDescription::VecInlineContent(inlines) => inlines.to_md(),
            ThingDescription::VecBlockContent(blocks) => blocks.to_md(),
        }
    }
}
<|endoftext|>


<commit_before>//! LLVM's simd platform intrinsics
//!
//! TODO: should use `link_llvm_intrinsic` instead: issue #112

extern "platform-intrinsic" {
    //pub fn simd_select_bitmask
    pub fn simd_eq<T, U>(x: T, y: T) -> U;
    pub fn simd_ne<T, U>(x: T, y: T) -> U;
    pub fn simd_lt<T, U>(x: T, y: T) -> U;
    pub fn simd_le<T, U>(x: T, y: T) -> U;
    pub fn simd_gt<T, U>(x: T, y: T) -> U;
    pub fn simd_ge<T, U>(x: T, y: T) -> U;

    #[rustc_args_required_const(2)]
    pub fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle64<T, U>(x: T, y: T, idx: [u32; 64]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle128<T, U>(x: T, y: T, idx: [u32; 128]) -> U;

    pub fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
    pub fn simd_extract<T, U>(x: T, idx: u32) -> U;
    //pub fn simd_select
    pub fn simd_bitmask<T, U>(x: T) -> U;

    pub fn simd_cast<T, U>(x: T) -> U;

    pub fn simd_add<T>(x: T, y: T) -> T;
    pub fn simd_sub<T>(x: T, y: T) -> T;
    pub fn simd_mul<T>(x: T, y: T) -> T;
    pub fn simd_div<T>(x: T, y: T) -> T;
    pub fn simd_shl<T>(x: T, y: T) -> T;
    pub fn simd_shr<T>(x: T, y: T) -> T;
    pub fn simd_and<T>(x: T, y: T) -> T;
    pub fn simd_or<T>(x: T, y: T) -> T;
    pub fn simd_xor<T>(x: T, y: T) -> T;

    pub fn simd_saturating_add<T>(x: T, y: T) -> T;
    pub fn simd_saturating_sub<T>(x: T, y: T) -> T;

    pub fn simd_gather<T, U, V>(values: T, pointers: U, mask: V) -> T;
    pub fn simd_scatter<T, U, V>(values: T, pointers: U, mask: V);

    pub fn simd_reduce_add_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_mul_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_add_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_mul_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_min<T, U>(x: T) -> U;
    pub fn simd_reduce_max<T, U>(x: T) -> U;
    pub fn simd_reduce_min_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_max_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_and<T, U>(x: T) -> U;
    pub fn simd_reduce_or<T, U>(x: T) -> U;
    pub fn simd_reduce_xor<T, U>(x: T) -> U;
    pub fn simd_reduce_all<T>(x: T) -> bool;
    pub fn simd_reduce_any<T>(x: T) -> bool;

    pub fn simd_select<M, T>(m: M, a: T, b: T) -> T;
    pub fn simd_select_bitmask<M, T>(m: M, a: T, b: T) -> T;

    pub fn simd_fmin<T>(a: T, b: T) -> T;
    pub fn simd_fmax<T>(a: T, b: T) -> T;

    pub fn simd_fsqrt<T>(a: T) -> T;
    pub fn simd_fsin<T>(a: T) -> T;
    pub fn simd_fcos<T>(a: T) -> T;
    pub fn simd_fabs<T>(a: T) -> T;
    pub fn simd_floor<T>(a: T) -> T;
    pub fn simd_ceil<T>(a: T) -> T;
    pub fn simd_fexp<T>(a: T) -> T;
    pub fn simd_fexp2<T>(a: T) -> T;
    pub fn simd_flog10<T>(a: T) -> T;
    pub fn simd_flog2<T>(a: T) -> T;
    pub fn simd_flog<T>(a: T) -> T;
    //pub fn simd_fpowi
    //pub fn simd_fpow
    pub fn simd_fma<T>(a: T, b: T, c: T) -> T;
}
<commit_msg>Add const unstability attributes<commit_after>//! LLVM's simd platform intrinsics
//!
//! TODO: should use `link_llvm_intrinsic` instead: issue #112

extern "platform-intrinsic" {
    //pub fn simd_select_bitmask
    pub fn simd_eq<T, U>(x: T, y: T) -> U;
    pub fn simd_ne<T, U>(x: T, y: T) -> U;
    pub fn simd_lt<T, U>(x: T, y: T) -> U;
    pub fn simd_le<T, U>(x: T, y: T) -> U;
    pub fn simd_gt<T, U>(x: T, y: T) -> U;
    pub fn simd_ge<T, U>(x: T, y: T) -> U;

    #[rustc_args_required_const(2)]
    pub fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle64<T, U>(x: T, y: T, idx: [u32; 64]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle128<T, U>(x: T, y: T, idx: [u32; 128]) -> U;

    #[rustc_const_unstable(feature = "const_simd_insert", issue = "0")]
    pub fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
    #[rustc_const_unstable(feature = "const_simd_extract", issue = "0")]
    pub fn simd_extract<T, U>(x: T, idx: u32) -> U;
    //pub fn simd_select
    pub fn simd_bitmask<T, U>(x: T) -> U;

    pub fn simd_cast<T, U>(x: T) -> U;

    pub fn simd_add<T>(x: T, y: T) -> T;
    pub fn simd_sub<T>(x: T, y: T) -> T;
    pub fn simd_mul<T>(x: T, y: T) -> T;
    pub fn simd_div<T>(x: T, y: T) -> T;
    pub fn simd_shl<T>(x: T, y: T) -> T;
    pub fn simd_shr<T>(x: T, y: T) -> T;
    pub fn simd_and<T>(x: T, y: T) -> T;
    pub fn simd_or<T>(x: T, y: T) -> T;
    pub fn simd_xor<T>(x: T, y: T) -> T;

    pub fn simd_saturating_add<T>(x: T, y: T) -> T;
    pub fn simd_saturating_sub<T>(x: T, y: T) -> T;

    pub fn simd_gather<T, U, V>(values: T, pointers: U, mask: V) -> T;
    pub fn simd_scatter<T, U, V>(values: T, pointers: U, mask: V);

    pub fn simd_reduce_add_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_mul_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_add_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_mul_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_min<T, U>(x: T) -> U;
    pub fn simd_reduce_max<T, U>(x: T) -> U;
    pub fn simd_reduce_min_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_max_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_and<T, U>(x: T) -> U;
    pub fn simd_reduce_or<T, U>(x: T) -> U;
    pub fn simd_reduce_xor<T, U>(x: T) -> U;
    pub fn simd_reduce_all<T>(x: T) -> bool;
    pub fn simd_reduce_any<T>(x: T) -> bool;

    pub fn simd_select<M, T>(m: M, a: T, b: T) -> T;
    pub fn simd_select_bitmask<M, T>(m: M, a: T, b: T) -> T;

    pub fn simd_fmin<T>(a: T, b: T) -> T;
    pub fn simd_fmax<T>(a: T, b: T) -> T;

    pub fn simd_fsqrt<T>(a: T) -> T;
    pub fn simd_fsin<T>(a: T) -> T;
    pub fn simd_fcos<T>(a: T) -> T;
    pub fn simd_fabs<T>(a: T) -> T;
    pub fn simd_floor<T>(a: T) -> T;
    pub fn simd_ceil<T>(a: T) -> T;
    pub fn simd_fexp<T>(a: T) -> T;
    pub fn simd_fexp2<T>(a: T) -> T;
    pub fn simd_flog10<T>(a: T) -> T;
    pub fn simd_flog2<T>(a: T) -> T;
    pub fn simd_flog<T>(a: T) -> T;
    //pub fn simd_fpowi
    //pub fn simd_fpow
    pub fn simd_fma<T>(a: T, b: T, c: T) -> T;
}
<|endoftext|>