<commit_before>use crate::cmd::connection::DEFAULT_DB;
use crate::cmd::{Durability, ReadMode};
use crate::proto::Payload;
use crate::{err, Connection, Query, Result, TcpStream};
use async_stream::try_stream;
use futures::channel::mpsc;
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use futures::sink::SinkExt;
use futures::stream::{Stream, StreamExt};
use log::trace;
use ql2::query::QueryType;
use ql2::response::{ErrorType, ResponseType};
use ql2::Frame;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::str;
use std::sync::atomic::Ordering;

const DATA_SIZE: usize = 4;
const TOKEN_SIZE: usize = 8;
const HEADER_SIZE: usize = DATA_SIZE + TOKEN_SIZE;

#[derive(Deserialize, Debug)]
pub struct Response {
    t: i32,
    e: Option<i32>,
    r: Value,
    b: Option<Vec<Frame>>,
    p: Option<Value>,
    n: Option<Vec<i32>>,
}

#[derive(Debug, Clone, Copy, Serialize, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub struct Options<'a> {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_mode: Option<ReadMode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub time_format: Option<Format>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub durability: Option<Durability>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub group_format: Option<Format>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub db: Option<Db<'a>>,
}

impl<'a> Options<'a> {
    pub fn new() -> Self {
        Default::default()
    }

    pub const fn db(mut self, db: &'a str) -> Self {
        self.db = Some(Db(db));
        self
    }
}

#[derive(Debug, Clone, Copy, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
pub enum Format {
    Native,
    Raw,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Db<'a>(pub &'a str);

pub trait Arg<'a, T> {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>);
}

impl<'a, T> Arg<'a, T> for &'a Connection<'a, T> {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>) {
        (self, None)
    }
}

impl<'a, T> Arg<'a, T> for (&'a Connection<'a, T>, Options<'a>) {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>) {
        (self.0, Some(self.1))
    }
}

pub(crate) fn new<'a, S, A, T>(query: Query, arg: A) -> impl Stream<Item = Result<T>>
where
    S: TcpStream<'a>,
    &'a S: AsyncRead + AsyncWrite,
    A: Arg<'a, S>,
    T: Unpin + DeserializeOwned,
{
    try_stream! {
        let (conn, mut opts) = arg.into();
        if conn.db != DEFAULT_DB {
            let mut options = opts.unwrap_or_else(|| Options::new());
            if options.db.is_none() {
                options = options.db(conn.db.as_ref());
            }
            opts = Some(options);
        }
        conn.broken()?;
        conn.change_feed()?;
        if query.change_feed() {
            conn.mark_change_feed();
        }
        let token = conn.token();
        let (tx, mut rx) = mpsc::channel(conn.buffer);
        conn.senders.insert(token, tx);
        let payload = Payload(QueryType::Start, Some(query), opts);
        trace!("sending query; token: {}, payload: {}", token, payload);
        conn.write(token, &payload).await?;
        trace!("query sent; token: {}", token);
        loop {
            trace!("receiving response; token: {}", token);
            let resp = conn.read(token, &mut rx).await?;
            let response_type = ResponseType::from_i32(resp.t)
                .ok_or_else(|| err::Client::Other(format!("uknown response type `{}`", resp.t)))?;
            if let Some(error_type) = resp.e {
                response_error(response_type, Some(error_type), resp)?;
                break;
            }
            match response_type {
                ResponseType::SuccessAtom | ResponseType::SuccessSequence | ResponseType::ServerInfo => {
                    for val in serde_json::from_value::<Vec<T>>(resp.r)? {
                        yield val;
                    }
                    break;
                }
                ResponseType::SuccessPartial => {
                    let payload = Payload(QueryType::Continue, None, None);
                    conn.write(token, &payload).await?;
                    for val in serde_json::from_value::<Vec<T>>(resp.r)? {
                        yield val;
                    }
                    continue;
                }
                // TODO what whould we yield here?
                ResponseType::WaitComplete => { break; }
                typ => {
                    response_error(typ, resp.e, resp)?;
                    break;
                }
            }
        }
        rx.close();
        conn.senders.remove(&token);
        conn.unmark_change_feed();
    }
}

impl<'a, S> Connection<'a, S>
where
    S: TcpStream<'a>,
    &'a S: AsyncRead + AsyncWrite,
{
    fn token(&self) -> u64 {
        let token = self
            .token
            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1))
            .unwrap();
        if token == u64::MAX {
            self.mark_broken();
        }
        token
    }

    async fn write(&'a self, token: u64, query: &Payload<'_>) -> Result<()> {
        let bytes = query.to_bytes()?;
        let data_len = bytes.len();
        let mut buf = Vec::with_capacity(HEADER_SIZE + data_len);
        buf.extend_from_slice(&token.to_le_bytes());
        buf.extend_from_slice(&(data_len as u32).to_le_bytes());
        buf.extend_from_slice(&bytes);
        (&mut &self.stream).write_all(&buf).await?;
        Ok(())
    }

    async fn read(
        &'a self,
        token: u64,
        rx: &mut mpsc::Receiver<Result<Response>>,
    ) -> Result<Response> {
        // reading data from RethinkDB is a 2 step process so
        // we have to get a lock to ensure no other process
        // reads the data while we are reading it
        let guard = self.locker.lock().await;
        trace!("reading header; token: {}", token);
        let mut header = [0u8; HEADER_SIZE];
        (&mut &self.stream).read(&mut header).await?;

        let mut buf = [0u8; TOKEN_SIZE];
        buf.copy_from_slice(&header[..TOKEN_SIZE]);
        let db_token = u64::from_le_bytes(buf);

        let mut buf = [0u8; DATA_SIZE];
        buf.copy_from_slice(&header[TOKEN_SIZE..]);
        let len = u32::from_le_bytes(buf) as usize;
        trace!(
            "header read; token: {}, db_token: {}, response_len: {}",
            token,
            db_token,
            len
        );

        trace!("reading body; token: {}", token);
        let mut buf = vec![0u8; len];
        let result = (&mut &self.stream).read(&mut buf).await;

        // we have finished reading so we can drop the lock
        // and let other processes advance
        drop(guard);

        trace!(
            "body {}; token: {}, db_token: {}",
            if result.is_ok() {
                "read"
            } else {
                "reading failed"
            },
            token,
            db_token,
        );

        match (result, db_token == token) {
            (result, true) => {
                result?;
                return Ok(serde_json::from_slice(&buf)?);
            }
            (Ok(_), false) => {
                let response = serde_json::from_slice(&buf).map_err(Into::into);
                self.send_response(db_token, response).await;
            }
            (Err(error), false) => {
                self.send_response(db_token, Err(error.into())).await;
            }
        }

        match rx.next().await {
            Some(resp) => resp,
            None => {
                Err(err::Client::Other("sender stream terminated prematurely".to_owned()).into())
            }
        }
    }

    async fn send_response(&self, db_token: u64, resp: Result<Response>) {
        if let Some(sender) = self.senders.get(&db_token) {
            let mut tx = sender.clone();
            if let Err(error) = tx.send(resp).await {
                if error.is_disconnected() {
                    self.senders.remove(&db_token);
                }
            }
        }
    }
}

fn response_error(
    response_type: ResponseType,
    error_type: Option<i32>,
    resp: Response,
) -> Result<()> {
    let msg = serde_json::from_value::<Vec<String>>(resp.r)?.join(" ");
    Err(match response_type {
        ResponseType::ClientError => err::Client::Other(msg).into(),
        ResponseType::CompileError => err::Error::Compile(msg),
        ResponseType::RuntimeError => match ErrorType::from_i32(
            error_type
                .ok_or_else(|| err::Client::Other(format!("unexpected runtime error: {}", msg)))?,
        ) {
            Some(ErrorType::Internal) => err::Runtime::Internal(msg).into(),
            Some(ErrorType::ResourceLimit) => err::Runtime::ResourceLimit(msg).into(),
            Some(ErrorType::QueryLogic) => err::Runtime::QueryLogic(msg).into(),
            Some(ErrorType::NonExistence) => err::Runtime::NonExistence(msg).into(),
            Some(ErrorType::OpFailed) => err::Availability::OpFailed(msg).into(),
            Some(ErrorType::OpIndeterminate) => err::Availability::OpIndeterminate(msg).into(),
            Some(ErrorType::User) => err::Runtime::User(msg).into(),
            Some(ErrorType::PermissionError) => err::Runtime::Permission(msg).into(),
            _ => err::Client::Other(format!("unexpected runtime error: {}", msg)).into(),
        },
        _ => err::Client::Other(format!("unexpected response: {}", msg)).into(),
    })
}
<commit_msg>make Response private<commit_after>use crate::cmd::connection::DEFAULT_DB;
use crate::cmd::{Durability, ReadMode};
use crate::proto::Payload;
use crate::{err, Connection, Query, Result, TcpStream};
use async_stream::try_stream;
use futures::channel::mpsc;
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use futures::sink::SinkExt;
use futures::stream::{Stream, StreamExt};
use log::trace;
use ql2::query::QueryType;
use ql2::response::{ErrorType, ResponseType};
use ql2::Frame;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::str;
use std::sync::atomic::Ordering;

const DATA_SIZE: usize = 4;
const TOKEN_SIZE: usize = 8;
const HEADER_SIZE: usize = DATA_SIZE + TOKEN_SIZE;

#[derive(Deserialize, Debug)]
pub(crate) struct Response {
    t: i32,
    e: Option<i32>,
    r: Value,
    b: Option<Vec<Frame>>,
    p: Option<Value>,
    n: Option<Vec<i32>>,
}

#[derive(Debug, Clone, Copy, Serialize, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub struct Options<'a> {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_mode: Option<ReadMode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub time_format: Option<Format>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub durability: Option<Durability>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub group_format: Option<Format>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub db: Option<Db<'a>>,
}

impl<'a> Options<'a> {
    pub fn new() -> Self {
        Default::default()
    }

    pub const fn db(mut self, db: &'a str) -> Self {
        self.db = Some(Db(db));
        self
    }
}

#[derive(Debug, Clone, Copy, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
pub enum Format {
    Native,
    Raw,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Db<'a>(pub &'a str);

pub trait Arg<'a, T> {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>);
}

impl<'a, T> Arg<'a, T> for &'a Connection<'a, T> {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>) {
        (self, None)
    }
}

impl<'a, T> Arg<'a, T> for (&'a Connection<'a, T>, Options<'a>) {
    fn into(self) -> (&'a Connection<'a, T>, Option<Options<'a>>) {
        (self.0, Some(self.1))
    }
}

pub(crate) fn new<'a, S, A, T>(query: Query, arg: A) -> impl Stream<Item = Result<T>>
where
    S: TcpStream<'a>,
    &'a S: AsyncRead + AsyncWrite,
    A: Arg<'a, S>,
    T: Unpin + DeserializeOwned,
{
    try_stream! {
        let (conn, mut opts) = arg.into();
        if conn.db != DEFAULT_DB {
            let mut options = opts.unwrap_or_else(|| Options::new());
            if options.db.is_none() {
                options = options.db(conn.db.as_ref());
            }
            opts = Some(options);
        }
        conn.broken()?;
        conn.change_feed()?;
        if query.change_feed() {
            conn.mark_change_feed();
        }
        let token = conn.token();
        let (tx, mut rx) = mpsc::channel(conn.buffer);
        conn.senders.insert(token, tx);
        let payload = Payload(QueryType::Start, Some(query), opts);
        trace!("sending query; token: {}, payload: {}", token, payload);
        conn.write(token, &payload).await?;
        trace!("query sent; token: {}", token);
        loop {
            trace!("receiving response; token: {}", token);
            let resp = conn.read(token, &mut rx).await?;
            let response_type = ResponseType::from_i32(resp.t)
                .ok_or_else(|| err::Client::Other(format!("uknown response type `{}`", resp.t)))?;
            if let Some(error_type) = resp.e {
                response_error(response_type, Some(error_type), resp)?;
                break;
            }
            match response_type {
                ResponseType::SuccessAtom | ResponseType::SuccessSequence | ResponseType::ServerInfo => {
                    for val in serde_json::from_value::<Vec<T>>(resp.r)? {
                        yield val;
                    }
                    break;
                }
                ResponseType::SuccessPartial => {
                    let payload = Payload(QueryType::Continue, None, None);
                    conn.write(token, &payload).await?;
                    for val in serde_json::from_value::<Vec<T>>(resp.r)? {
                        yield val;
                    }
                    continue;
                }
                // TODO what whould we yield here?
                ResponseType::WaitComplete => { break; }
                typ => {
                    response_error(typ, resp.e, resp)?;
                    break;
                }
            }
        }
        rx.close();
        conn.senders.remove(&token);
        conn.unmark_change_feed();
    }
}

impl<'a, S> Connection<'a, S>
where
    S: TcpStream<'a>,
    &'a S: AsyncRead + AsyncWrite,
{
    fn token(&self) -> u64 {
        let token = self
            .token
            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1))
            .unwrap();
        if token == u64::MAX {
            self.mark_broken();
        }
        token
    }

    async fn write(&'a self, token: u64, query: &Payload<'_>) -> Result<()> {
        let bytes = query.to_bytes()?;
        let data_len = bytes.len();
        let mut buf = Vec::with_capacity(HEADER_SIZE + data_len);
        buf.extend_from_slice(&token.to_le_bytes());
        buf.extend_from_slice(&(data_len as u32).to_le_bytes());
        buf.extend_from_slice(&bytes);
        (&mut &self.stream).write_all(&buf).await?;
        Ok(())
    }

    async fn read(
        &'a self,
        token: u64,
        rx: &mut mpsc::Receiver<Result<Response>>,
    ) -> Result<Response> {
        // reading data from RethinkDB is a 2 step process so
        // we have to get a lock to ensure no other process
        // reads the data while we are reading it
        let guard = self.locker.lock().await;
        trace!("reading header; token: {}", token);
        let mut header = [0u8; HEADER_SIZE];
        (&mut &self.stream).read(&mut header).await?;

        let mut buf = [0u8; TOKEN_SIZE];
        buf.copy_from_slice(&header[..TOKEN_SIZE]);
        let db_token = u64::from_le_bytes(buf);

        let mut buf = [0u8; DATA_SIZE];
        buf.copy_from_slice(&header[TOKEN_SIZE..]);
        let len = u32::from_le_bytes(buf) as usize;
        trace!(
            "header read; token: {}, db_token: {}, response_len: {}",
            token,
            db_token,
            len
        );

        trace!("reading body; token: {}", token);
        let mut buf = vec![0u8; len];
        let result = (&mut &self.stream).read(&mut buf).await;

        // we have finished reading so we can drop the lock
        // and let other processes advance
        drop(guard);

        trace!(
            "body {}; token: {}, db_token: {}",
            if result.is_ok() {
                "read"
            } else {
                "reading failed"
            },
            token,
            db_token,
        );

        match (result, db_token == token) {
            (result, true) => {
                result?;
                return Ok(serde_json::from_slice(&buf)?);
            }
            (Ok(_), false) => {
                let response = serde_json::from_slice(&buf).map_err(Into::into);
                self.send_response(db_token, response).await;
            }
            (Err(error), false) => {
                self.send_response(db_token, Err(error.into())).await;
            }
        }

        match rx.next().await {
            Some(resp) => resp,
            None => {
                Err(err::Client::Other("sender stream terminated prematurely".to_owned()).into())
            }
        }
    }

    async fn send_response(&self, db_token: u64, resp: Result<Response>) {
        if let Some(sender) = self.senders.get(&db_token) {
            let mut tx = sender.clone();
            if let Err(error) = tx.send(resp).await {
                if error.is_disconnected() {
                    self.senders.remove(&db_token);
                }
            }
        }
    }
}

fn response_error(
    response_type: ResponseType,
    error_type: Option<i32>,
    resp: Response,
) -> Result<()> {
    let msg = serde_json::from_value::<Vec<String>>(resp.r)?.join(" ");
    Err(match response_type {
        ResponseType::ClientError => err::Client::Other(msg).into(),
        ResponseType::CompileError => err::Error::Compile(msg),
        ResponseType::RuntimeError => match ErrorType::from_i32(
            error_type
                .ok_or_else(|| err::Client::Other(format!("unexpected runtime error: {}", msg)))?,
        ) {
            Some(ErrorType::Internal) => err::Runtime::Internal(msg).into(),
            Some(ErrorType::ResourceLimit) => err::Runtime::ResourceLimit(msg).into(),
            Some(ErrorType::QueryLogic) => err::Runtime::QueryLogic(msg).into(),
            Some(ErrorType::NonExistence) => err::Runtime::NonExistence(msg).into(),
            Some(ErrorType::OpFailed) => err::Availability::OpFailed(msg).into(),
            Some(ErrorType::OpIndeterminate) => err::Availability::OpIndeterminate(msg).into(),
            Some(ErrorType::User) => err::Runtime::User(msg).into(),
            Some(ErrorType::PermissionError) => err::Runtime::Permission(msg).into(),
            _ => err::Client::Other(format!("unexpected runtime error: {}", msg)).into(),
        },
        _ => err::Client::Other(format!("unexpected response: {}", msg)).into(),
    })
}
<|endoftext|>


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

use crate::common;
use libra_types::transaction::{ArgumentABI, ScriptABI, TypeArgumentABI};
use move_core_types::language_storage::TypeTag;
use serde_generate::indent::{IndentConfig, IndentedWriter};

use std::{
    io::{Result, Write},
    path::PathBuf,
};

/// Output a header-only library providing C++ transaction builders for the given ABIs.
pub fn output(out: &mut dyn Write, abis: &[ScriptABI], namespace: Option<&str>) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: true,
    };
    emitter.output_preamble()?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_definition(abi)?;
    }
    emitter.output_close_namespace()
}

/// Output the headers of a library providing C++ transaction builders for the given ABIs.
pub fn output_library_header(
    out: &mut dyn Write,
    abis: &[ScriptABI],
    namespace: Option<&str>,
) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: true,
    };
    emitter.output_preamble()?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_declaration(abi)?;
    }
    emitter.output_close_namespace()
}

/// Output the function definitions of a library providing C++ transaction builders for the given ABIs.
pub fn output_library_body(
    out: &mut dyn Write,
    abis: &[ScriptABI],
    library_name: &str,
    namespace: Option<&str>,
) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: false,
    };
    writeln!(emitter.out, "#include \"{}.hpp\"\n", library_name)?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_definition(abi)?;
    }
    emitter.output_close_namespace()
}

/// Shared state for the Cpp code generator.
struct CppEmitter<'a, T> {
    /// Writer.
    out: IndentedWriter<T>,
    /// Name of the package owning the generated definitions (e.g. "com.facebook.my_package")
    namespace: Option<&'a str>,
    /// Whether function definitions should be prefixed with "inlined"
    inlined_definitions: bool,
}

impl<'a, T> CppEmitter<'a, T>
where
    T: Write,
{
    fn output_preamble(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"
#pragma once

#include "libra_types.hpp"
"#
        )
    }

    fn output_using_namespaces(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"
using namespace serde;
using namespace libra_types;
"#
        )
    }

    fn output_open_namespace(&mut self) -> Result<()> {
        if let Some(name) = self.namespace {
            writeln!(self.out, "namespace {} {{\n", name)?;
        }
        Ok(())
    }

    fn output_close_namespace(&mut self) -> Result<()> {
        if let Some(name) = self.namespace {
            writeln!(self.out, "\n}} // end of namespace {}", name)?;
        }
        Ok(())
    }

    fn output_builder_declaration(&mut self, abi: &ScriptABI) -> Result<()> {
        write!(self.out, "\n{}", Self::quote_doc(abi.doc()))?;
        writeln!(
            self.out,
            "Script encode_{}_script({});",
            abi.name(),
            [
                Self::quote_type_parameters(abi.ty_args()),
                Self::quote_parameters(abi.args()),
            ]
            .concat()
            .join(", ")
        )?;
        Ok(())
    }

    fn output_builder_definition(&mut self, abi: &ScriptABI) -> Result<()> {
        if self.inlined_definitions {
            write!(self.out, "\n{}", Self::quote_doc(abi.doc()))?;
        }
        writeln!(
            self.out,
            "{}Script encode_{}_script({}) {{",
            if self.inlined_definitions {
                "inline "
            } else {
                ""
            },
            abi.name(),
            [
                Self::quote_type_parameters(abi.ty_args()),
                Self::quote_parameters(abi.args()),
            ]
            .concat()
            .join(", ")
        )?;
        writeln!(
            self.out,
            r#"    return Script {{
        {},
        std::vector<TypeTag> {{{}}},
        std::vector<TransactionArgument> {{{}}},
    }};"#,
            Self::quote_code(abi.code()),
            Self::quote_type_arguments(abi.ty_args()),
            Self::quote_arguments(abi.args()),
        )?;
        writeln!(self.out, "}}")?;
        Ok(())
    }

    fn quote_doc(doc: &str) -> String {
        let doc = crate::common::prepare_doc_string(doc);
        textwrap::indent(&doc, "/// ")
    }

    fn quote_type_parameters(ty_args: &[TypeArgumentABI]) -> Vec<String> {
        ty_args
            .iter()
            .map(|ty_arg| format!("TypeTag {}", ty_arg.name()))
            .collect()
    }

    fn quote_parameters(args: &[ArgumentABI]) -> Vec<String> {
        args.iter()
            .map(|arg| format!("{} {}", Self::quote_type(arg.type_tag()), arg.name()))
            .collect()
    }

    fn quote_code(code: &[u8]) -> String {
        format!(
            "std::vector<uint8_t> {{{}}}",
            code.iter()
                .map(|x| format!("{}", x))
                .collect::<Vec<_>>()
                .join(", ")
        )
    }

    fn quote_type_arguments(ty_args: &[TypeArgumentABI]) -> String {
        ty_args
            .iter()
            .map(|ty_arg| format!("std::move({})", ty_arg.name()))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn quote_arguments(args: &[ArgumentABI]) -> String {
        args.iter()
            .map(|arg| Self::quote_transaction_argument(arg.type_tag(), arg.name()))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn quote_type(type_tag: &TypeTag) -> String {
        use TypeTag::*;
        match type_tag {
            Bool => "bool".into(),
            U8 => "uint8_t".into(),
            U64 => "uint64_t".into(),
            U128 => "uint128_t".into(),
            Address => "AccountAddress".into(),
            Vector(type_tag) => match type_tag.as_ref() {
                U8 => "std::vector<uint8_t>".into(),
                _ => common::type_not_allowed(type_tag),
            },
            Struct(_) | Signer => common::type_not_allowed(type_tag),
        }
    }

    fn quote_transaction_argument(type_tag: &TypeTag, name: &str) -> String {
        use TypeTag::*;
        match type_tag {
            Bool => format!("{{TransactionArgument::Bool {{{}}} }}", name),
            U8 => format!("{{TransactionArgument::U8 {{{}}} }}", name),
            U64 => format!("{{TransactionArgument::U64 {{{}}} }}", name),
            U128 => format!("{{TransactionArgument::U128 {{{}}} }}", name),
            // Adding std::move in the non-obvious cases to be future-proof.
            Address => format!("{{TransactionArgument::Address {{std::move({})}}}}", name),
            Vector(type_tag) => match type_tag.as_ref() {
                U8 => format!("{{TransactionArgument::U8Vector {{std::move({})}}}}", name),
                _ => common::type_not_allowed(type_tag),
            },

            Struct(_) | Signer => common::type_not_allowed(type_tag),
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
}

impl Installer {
    pub fn new(install_dir: PathBuf) -> Self {
        Installer { install_dir }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_transaction_builders(
        &self,
        name: &str,
        abis: &[ScriptABI],
    ) -> std::result::Result<(), Self::Error> {
        let dir_path = &self.install_dir;
        std::fs::create_dir_all(dir_path)?;
        let header_path = dir_path.join(name.to_string() + ".hpp");
        let mut header = std::fs::File::create(&header_path)?;
        output_library_header(&mut header, abis, Some(name))?;
        let body_path = dir_path.join(name.to_string() + ".cpp");
        let mut body = std::fs::File::create(&body_path)?;
        output_library_body(&mut body, abis, name, Some(name))?;
        Ok(())
    }
}
<commit_msg>[transaction builder generator] c++ nits<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::common;
use libra_types::transaction::{ArgumentABI, ScriptABI, TypeArgumentABI};
use move_core_types::language_storage::TypeTag;
use serde_generate::indent::{IndentConfig, IndentedWriter};

use std::{
    io::{Result, Write},
    path::PathBuf,
};

/// Output a header-only library providing C++ transaction builders for the given ABIs.
pub fn output(out: &mut dyn Write, abis: &[ScriptABI], namespace: Option<&str>) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: true,
    };
    emitter.output_preamble()?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_definition(abi)?;
    }
    emitter.output_close_namespace()
}

/// Output the headers of a library providing C++ transaction builders for the given ABIs.
pub fn output_library_header(
    out: &mut dyn Write,
    abis: &[ScriptABI],
    namespace: Option<&str>,
) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: true,
    };
    emitter.output_preamble()?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_declaration(abi)?;
    }
    emitter.output_close_namespace()
}

/// Output the function definitions of a library providing C++ transaction builders for the given ABIs.
pub fn output_library_body(
    out: &mut dyn Write,
    abis: &[ScriptABI],
    library_name: &str,
    namespace: Option<&str>,
) -> Result<()> {
    let mut emitter = CppEmitter {
        out: IndentedWriter::new(out, IndentConfig::Space(4)),
        namespace,
        inlined_definitions: false,
    };
    writeln!(emitter.out, "#include \"{}.hpp\"\n", library_name)?;
    emitter.output_open_namespace()?;
    emitter.output_using_namespaces()?;
    for abi in abis {
        emitter.output_builder_definition(abi)?;
    }
    emitter.output_close_namespace()
}

/// Shared state for the Cpp code generator.
struct CppEmitter<'a, T> {
    /// Writer.
    out: IndentedWriter<T>,
    /// Name of the package owning the generated definitions (e.g. "com.facebook.my_package")
    namespace: Option<&'a str>,
    /// Whether function definitions should be prefixed with "inlined"
    inlined_definitions: bool,
}

impl<'a, T> CppEmitter<'a, T>
where
    T: Write,
{
    fn output_preamble(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"#pragma once

#include "libra_types.hpp"
"#
        )
    }

    fn output_using_namespaces(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"
using namespace serde;
using namespace libra_types;
"#
        )
    }

    fn output_open_namespace(&mut self) -> Result<()> {
        if let Some(name) = self.namespace {
            writeln!(self.out, "namespace {} {{\n", name)?;
        }
        Ok(())
    }

    fn output_close_namespace(&mut self) -> Result<()> {
        if let Some(name) = self.namespace {
            writeln!(self.out, "\n}} // end of namespace {}", name)?;
        }
        Ok(())
    }

    fn output_builder_declaration(&mut self, abi: &ScriptABI) -> Result<()> {
        write!(self.out, "\n{}", Self::quote_doc(abi.doc()))?;
        writeln!(
            self.out,
            "Script encode_{}_script({});",
            abi.name(),
            [
                Self::quote_type_parameters(abi.ty_args()),
                Self::quote_parameters(abi.args()),
            ]
            .concat()
            .join(", ")
        )?;
        Ok(())
    }

    fn output_builder_definition(&mut self, abi: &ScriptABI) -> Result<()> {
        if self.inlined_definitions {
            write!(self.out, "\n{}", Self::quote_doc(abi.doc()))?;
        }
        writeln!(
            self.out,
            "{}Script encode_{}_script({}) {{",
            if self.inlined_definitions {
                "inline "
            } else {
                ""
            },
            abi.name(),
            [
                Self::quote_type_parameters(abi.ty_args()),
                Self::quote_parameters(abi.args()),
            ]
            .concat()
            .join(", ")
        )?;
        writeln!(
            self.out,
            r#"    return Script {{
        {},
        std::vector<TypeTag> {{{}}},
        std::vector<TransactionArgument> {{{}}},
    }};"#,
            Self::quote_code(abi.code()),
            Self::quote_type_arguments(abi.ty_args()),
            Self::quote_arguments(abi.args()),
        )?;
        writeln!(self.out, "}}")?;
        Ok(())
    }

    fn quote_doc(doc: &str) -> String {
        let doc = crate::common::prepare_doc_string(doc);
        textwrap::indent(&doc, "/// ").replace("\n\n", "\n///\n")
    }

    fn quote_type_parameters(ty_args: &[TypeArgumentABI]) -> Vec<String> {
        ty_args
            .iter()
            .map(|ty_arg| format!("TypeTag {}", ty_arg.name()))
            .collect()
    }

    fn quote_parameters(args: &[ArgumentABI]) -> Vec<String> {
        args.iter()
            .map(|arg| format!("{} {}", Self::quote_type(arg.type_tag()), arg.name()))
            .collect()
    }

    fn quote_code(code: &[u8]) -> String {
        format!(
            "std::vector<uint8_t> {{{}}}",
            code.iter()
                .map(|x| format!("{}", x))
                .collect::<Vec<_>>()
                .join(", ")
        )
    }

    fn quote_type_arguments(ty_args: &[TypeArgumentABI]) -> String {
        ty_args
            .iter()
            .map(|ty_arg| format!("std::move({})", ty_arg.name()))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn quote_arguments(args: &[ArgumentABI]) -> String {
        args.iter()
            .map(|arg| Self::quote_transaction_argument(arg.type_tag(), arg.name()))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn quote_type(type_tag: &TypeTag) -> String {
        use TypeTag::*;
        match type_tag {
            Bool => "bool".into(),
            U8 => "uint8_t".into(),
            U64 => "uint64_t".into(),
            U128 => "uint128_t".into(),
            Address => "AccountAddress".into(),
            Vector(type_tag) => match type_tag.as_ref() {
                U8 => "std::vector<uint8_t>".into(),
                _ => common::type_not_allowed(type_tag),
            },
            Struct(_) | Signer => common::type_not_allowed(type_tag),
        }
    }

    fn quote_transaction_argument(type_tag: &TypeTag, name: &str) -> String {
        use TypeTag::*;
        match type_tag {
            Bool => format!("{{TransactionArgument::Bool {{{}}} }}", name),
            U8 => format!("{{TransactionArgument::U8 {{{}}} }}", name),
            U64 => format!("{{TransactionArgument::U64 {{{}}} }}", name),
            U128 => format!("{{TransactionArgument::U128 {{{}}} }}", name),
            // Adding std::move in the non-obvious cases to be future-proof.
            Address => format!("{{TransactionArgument::Address {{std::move({})}}}}", name),
            Vector(type_tag) => match type_tag.as_ref() {
                U8 => format!("{{TransactionArgument::U8Vector {{std::move({})}}}}", name),
                _ => common::type_not_allowed(type_tag),
            },

            Struct(_) | Signer => common::type_not_allowed(type_tag),
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
}

impl Installer {
    pub fn new(install_dir: PathBuf) -> Self {
        Installer { install_dir }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_transaction_builders(
        &self,
        name: &str,
        abis: &[ScriptABI],
    ) -> std::result::Result<(), Self::Error> {
        let dir_path = &self.install_dir;
        std::fs::create_dir_all(dir_path)?;
        let header_path = dir_path.join(name.to_string() + ".hpp");
        let mut header = std::fs::File::create(&header_path)?;
        output_library_header(&mut header, abis, Some(name))?;
        let body_path = dir_path.join(name.to_string() + ".cpp");
        let mut body = std::fs::File::create(&body_path)?;
        output_library_body(&mut body, abis, name, Some(name))?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>// @generated by autocargo
use std::env;
use std::fs;
use std::path::Path;

use thrift_compiler::Config;

#[rustfmt::skip]
fn main() {
    let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR env not provided");
    let out_dir: &Path = out_dir.as_ref();
    fs::write(
        out_dir.join("cratemap"),
        "test_thrift crate",
    ).expect("Failed to write cratemap");

    let conf = {
        let mut conf = Config::from_env().expect("Failed to instantiate thrift_compiler::Config");

        let path_from_manifest_to_base: &Path = "../../../../../..".as_ref();
        let cargo_manifest_dir =
            env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not provided");
        let cargo_manifest_dir: &Path = cargo_manifest_dir.as_ref();
        let base_path = cargo_manifest_dir
            .join(path_from_manifest_to_base)
            .canonicalize()
            .expect("Failed to canonicalize base_path");
        conf.base_path(base_path);

        let options = "serde";
        if !options.is_empty() {
            conf.options(options);
        }

        let include_srcs = vec![

        ];
        conf.include_srcs(include_srcs);

        conf
    };

    conf
        .run(&[
            "../test_thrift.thrift"
        ])
        .expect("Failed while running thrift compilation");
}
<commit_msg>Update autocargo component on FBS:master<commit_after>// @generated by autocargo
use std::env;
use std::fs;
use std::path::Path;

use thrift_compiler::Config;

#[rustfmt::skip]
fn main() {
    // Rerun if this gets rewritten.
    println!("cargo:rerun-if-changed=thrift_build.rs");

    let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR env not provided");
    let out_dir: &Path = out_dir.as_ref();
    fs::write(
        out_dir.join("cratemap"),
        "test_thrift crate",
    ).expect("Failed to write cratemap");

    let conf = {
        let mut conf = Config::from_env().expect("Failed to instantiate thrift_compiler::Config");

        let path_from_manifest_to_base: &Path = "../../../../../..".as_ref();
        let cargo_manifest_dir =
            env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not provided");
        let cargo_manifest_dir: &Path = cargo_manifest_dir.as_ref();
        let base_path = cargo_manifest_dir
            .join(path_from_manifest_to_base)
            .canonicalize()
            .expect("Failed to canonicalize base_path");
        conf.base_path(base_path);

        let options = "serde";
        if !options.is_empty() {
            conf.options(options);
        }

        let include_srcs = vec![

        ];
        conf.include_srcs(include_srcs);

        conf
    };

    conf
        .run(&[
            "../test_thrift.thrift"
        ])
        .expect("Failed while running thrift compilation");
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use std::iter::successors;
use syntax::{
    algo::neighbor,
    ast::{self, AstNode},
    Direction,
};

use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};

// Assist: merge_match_arms
//
// Merges the current match arm with the following if their bodies are identical.
//
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         $0Action::Move(..) => foo(),
//         Action::Stop => foo(),
//     }
// }
// ```
// ->
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         Action::Move(..) | Action::Stop => foo(),
//     }
// }
// ```
pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
    // Don't try to handle arms with guards for now - can add support for this later
    if current_arm.guard().is_some() {
        return None;
    }
    let current_expr = current_arm.expr()?;
    let current_text_range = current_arm.syntax().text_range();
    let current_arm_types = get_arm_types(&ctx, &current_arm);

    // We check if the following match arms match this one. We could, but don't,
    // compare to the previous match arm as well.
    let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
        .take_while(|arm| match arm.expr() {
            Some(expr) if arm.guard().is_none() => {
                let same_text = expr.syntax().text() == current_expr.syntax().text();
                if !same_text {
                    return false;
                }

                return are_same_types(&current_arm_types, arm, ctx);
            }
            _ => false,
        })
        .collect::<Vec<_>>();

    if arms_to_merge.len() <= 1 {
        return None;
    }

    acc.add(
        AssistId("merge_match_arms", AssistKind::RefactorRewrite),
        "Merge match arms",
        current_text_range,
        |edit| {
            let pats = if arms_to_merge.iter().any(contains_placeholder) {
                "_".into()
            } else {
                arms_to_merge
                    .iter()
                    .filter_map(ast::MatchArm::pat)
                    .map(|x| x.syntax().to_string())
                    .collect::<Vec<String>>()
                    .join(" | ")
            };

            let arm = format!("{} => {},", pats, current_expr.syntax().text());

            if let [first, .., last] = &*arms_to_merge {
                let start = first.syntax().text_range().start();
                let end = last.syntax().text_range().end();

                edit.replace(TextRange::new(start, end), arm);
            }
        },
    )
}

fn contains_placeholder(a: &ast::MatchArm) -> bool {
    matches!(a.pat(), Some(ast::Pat::WildcardPat(..)))
}

fn are_same_types(
    current_arm_types: &Vec<Option<hir::TypeInfo>>,
    arm: &ast::MatchArm,
    ctx: &AssistContext,
) -> bool {
    let arm_types = get_arm_types(&ctx, &arm);
    for i in 0..arm_types.len() {
        let other_arm_type = &arm_types[i];
        let current_arm_type = &current_arm_types[i];
        if let (Some(other_arm_type), Some(current_arm_type)) = (other_arm_type, current_arm_type) {
            return &other_arm_type.original == &current_arm_type.original;
        }
    }

    return true;
}

fn get_arm_types(ctx: &AssistContext, arm: &ast::MatchArm) -> Vec<Option<hir::TypeInfo>> {
    match arm.pat() {
        Some(ast::Pat::TupleStructPat(tp)) => {
            tp.fields().into_iter().map(|field| ctx.sema.type_of_pat(&field)).collect_vec()
        }
        _ => Vec::new(),
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn merge_match_arms_single_patterns() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32$0 }
        X::B => { 1i32 }
        X::C => { 2i32 }
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B => { 1i32 },
        X::C => { 2i32 }
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_multiple_patterns() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B => {$0 1i32 },
        X::C | X::D => { 1i32 },
        X::E => { 2i32 },
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B | X::C | X::D => { 1i32 },
        X::E => { 2i32 },
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_placeholder_pattern() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32 },
        X::B => { 2i$032 },
        _ => { 2i32 }
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32 },
        _ => { 2i32 },
    }
}
"#,
        );
    }

    #[test]
    fn merges_all_subsequent_arms() {
        check_assist(
            merge_match_arms,
            r#"
enum X { A, B, C, D, E }

fn main() {
    match X::A {
        X::A$0 => 92,
        X::B => 92,
        X::C => 92,
        X::D => 62,
        _ => panic!(),
    }
}
"#,
            r#"
enum X { A, B, C, D, E }

fn main() {
    match X::A {
        X::A | X::B | X::C => 92,
        X::D => 62,
        _ => panic!(),
    }
}
"#,
        )
    }

    #[test]
    fn merge_match_arms_rejects_guards() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X {
    A(i32),
    B,
    C
}

fn main() {
    let x = X::A;
    let y = match x {
        X::A(a) if a > 5 => { $01i32 },
        X::B => { 1i32 },
        X::C => { 2i32 }
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_different_type() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<f64, f32>::Ok(0f64) {
        Ok(x) => $0x.classify(),
        Err(x) => x.classify()
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_different_type_multiple_fields() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<(f64, f64), (f32, f32)>::Ok((0f64, 0f64)) {
        Ok(x) => $0x.1.classify(),
        Err(x) => x.1.classify()
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_same_type_multiple_fields() {
        check_assist(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
        Ok(x) => $0x.1.classify(),
        Err(x) => x.1.classify()
    };
}
"#,
            r#"
fn func() {
    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
        Ok(x) | Err(x) => x.1.classify(),
    };
}
"#,
        );
    }
}
<commit_msg>test cases with more branches<commit_after>use itertools::Itertools;
use std::iter::successors;
use syntax::{
    algo::neighbor,
    ast::{self, AstNode},
    Direction,
};

use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};

// Assist: merge_match_arms
//
// Merges the current match arm with the following if their bodies are identical.
//
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         $0Action::Move(..) => foo(),
//         Action::Stop => foo(),
//     }
// }
// ```
// ->
// ```
// enum Action { Move { distance: u32 }, Stop }
//
// fn handle(action: Action) {
//     match action {
//         Action::Move(..) | Action::Stop => foo(),
//     }
// }
// ```
pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
    // Don't try to handle arms with guards for now - can add support for this later
    if current_arm.guard().is_some() {
        return None;
    }
    let current_expr = current_arm.expr()?;
    let current_text_range = current_arm.syntax().text_range();
    let current_arm_types = get_arm_types(&ctx, &current_arm);

    // We check if the following match arms match this one. We could, but don't,
    // compare to the previous match arm as well.
    let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
        .take_while(|arm| match arm.expr() {
            Some(expr) if arm.guard().is_none() => {
                let same_text = expr.syntax().text() == current_expr.syntax().text();
                if !same_text {
                    return false;
                }

                return are_same_types(&current_arm_types, arm, ctx);
            }
            _ => false,
        })
        .collect::<Vec<_>>();

    if arms_to_merge.len() <= 1 {
        return None;
    }

    acc.add(
        AssistId("merge_match_arms", AssistKind::RefactorRewrite),
        "Merge match arms",
        current_text_range,
        |edit| {
            let pats = if arms_to_merge.iter().any(contains_placeholder) {
                "_".into()
            } else {
                arms_to_merge
                    .iter()
                    .filter_map(ast::MatchArm::pat)
                    .map(|x| x.syntax().to_string())
                    .collect::<Vec<String>>()
                    .join(" | ")
            };

            let arm = format!("{} => {},", pats, current_expr.syntax().text());

            if let [first, .., last] = &*arms_to_merge {
                let start = first.syntax().text_range().start();
                let end = last.syntax().text_range().end();

                edit.replace(TextRange::new(start, end), arm);
            }
        },
    )
}

fn contains_placeholder(a: &ast::MatchArm) -> bool {
    matches!(a.pat(), Some(ast::Pat::WildcardPat(..)))
}

fn are_same_types(
    current_arm_types: &Vec<Option<hir::TypeInfo>>,
    arm: &ast::MatchArm,
    ctx: &AssistContext,
) -> bool {
    let arm_types = get_arm_types(&ctx, &arm);
    for i in 0..arm_types.len() {
        let other_arm_type = &arm_types[i];
        let current_arm_type = &current_arm_types[i];
        if let (Some(other_arm_type), Some(current_arm_type)) = (other_arm_type, current_arm_type) {
            return &other_arm_type.original == &current_arm_type.original;
        }
    }

    return true;
}

fn get_arm_types(ctx: &AssistContext, arm: &ast::MatchArm) -> Vec<Option<hir::TypeInfo>> {
    match arm.pat() {
        Some(ast::Pat::TupleStructPat(tp)) => {
            tp.fields().into_iter().map(|field| ctx.sema.type_of_pat(&field)).collect_vec()
        }
        _ => Vec::new(),
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn merge_match_arms_single_patterns() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32$0 }
        X::B => { 1i32 }
        X::C => { 2i32 }
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B => { 1i32 },
        X::C => { 2i32 }
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_multiple_patterns() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B => {$0 1i32 },
        X::C | X::D => { 1i32 },
        X::E => { 2i32 },
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A | X::B | X::C | X::D => { 1i32 },
        X::E => { 2i32 },
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_placeholder_pattern() {
        check_assist(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32 },
        X::B => { 2i$032 },
        _ => { 2i32 }
    }
}
"#,
            r#"
#[derive(Debug)]
enum X { A, B, C, D, E }

fn main() {
    let x = X::A;
    let y = match x {
        X::A => { 1i32 },
        _ => { 2i32 },
    }
}
"#,
        );
    }

    #[test]
    fn merges_all_subsequent_arms() {
        check_assist(
            merge_match_arms,
            r#"
enum X { A, B, C, D, E }

fn main() {
    match X::A {
        X::A$0 => 92,
        X::B => 92,
        X::C => 92,
        X::D => 62,
        _ => panic!(),
    }
}
"#,
            r#"
enum X { A, B, C, D, E }

fn main() {
    match X::A {
        X::A | X::B | X::C => 92,
        X::D => 62,
        _ => panic!(),
    }
}
"#,
        )
    }

    #[test]
    fn merge_match_arms_rejects_guards() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"
#[derive(Debug)]
enum X {
    A(i32),
    B,
    C
}

fn main() {
    let x = X::A;
    let y = match x {
        X::A(a) if a > 5 => { $01i32 },
        X::B => { 1i32 },
        X::C => { 2i32 }
    }
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_different_type() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<f64, f32>::Ok(0f64) {
        Ok(x) => $0x.classify(),
        Err(x) => x.classify()
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_different_type_multiple_fields() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<(f64, f64), (f32, f32)>::Ok((0f64, 0f64)) {
        Ok(x) => $0x.1.classify(),
        Err(x) => x.1.classify()
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_same_type_multiple_fields() {
        check_assist(
            merge_match_arms,
            r#"//- minicore: result
fn func() {
    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
        Ok(x) => $0x.1.classify(),
        Err(x) => x.1.classify()
    };
}
"#,
            r#"
fn func() {
    match Result::<(f64, f64), (f64, f64)>::Ok((0f64, 0f64)) {
        Ok(x) | Err(x) => x.1.classify(),
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_same_type_subsequent_arm_with_different_type_in_other() {
        check_assist(
            merge_match_arms,
            r#"
enum MyEnum {
    OptionA(f32),
    OptionB(f32),
    OptionC(f64)
}

fn func(e: MyEnum) {
    match e {
        MyEnum::OptionA(x) => $0x.classify(),
        MyEnum::OptionB(x) => x.classify(),
        MyEnum::OptionC(x) => x.classify(),
    };
}
"#,
            r#"
enum MyEnum {
    OptionA(f32),
    OptionB(f32),
    OptionC(f64)
}

fn func(e: MyEnum) {
    match e {
        MyEnum::OptionA(x) | MyEnum::OptionB(x) => x.classify(),
        MyEnum::OptionC(x) => x.classify(),
    };
}
"#,
        );
    }

    #[test]
    fn merge_match_arms_same_type_skip_arm_with_different_type_in_between() {
        check_assist_not_applicable(
            merge_match_arms,
            r#"
enum MyEnum {
    OptionA(f32),
    OptionB(f64),
    OptionC(f32)
}

fn func(e: MyEnum) {
    match e {
        MyEnum::OptionA(x) => $0x.classify(),
        MyEnum::OptionB(x) => x.classify(),
        MyEnum::OptionC(x) => x.classify(),
    };
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>//! Advertises the capabilities of the LSP Server.
use lsp_types::{
    CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
    DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
    FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
    HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
    SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
    WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
    WorkspaceServerCapabilities,
};
use serde_json::json;

use crate::config::{Config, RustfmtConfig};
use crate::semantic_tokens;

pub fn server_capabilities(config: &Config) -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::INCREMENTAL),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default().into()),
        })),
        hover_provider: Some(HoverProviderCapability::Simple(true)),
        completion_provider: Some(CompletionOptions {
            resolve_provider: completions_resolve_provider(&config.caps),
            trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
            all_commit_characters: None,
            completion_item: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: Some(DeclarationCapability::Simple(true)),
        definition_provider: Some(OneOf::Left(true)),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(OneOf::Left(true)),
        document_highlight_provider: Some(OneOf::Left(true)),
        document_symbol_provider: Some(OneOf::Left(true)),
        workspace_symbol_provider: Some(OneOf::Left(true)),
        code_action_provider: Some(code_action_capabilities(&config.caps)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(OneOf::Left(true)),
        document_range_formatting_provider: match config.rustfmt() {
            RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
            _ => Some(OneOf::Left(false)),
        },
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(OneOf::Right(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        linked_editing_range_provider: None,
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: Some(WorkspaceServerCapabilities {
            workspace_folders: None,
            file_operations: Some(WorkspaceFileOperationsServerCapabilities {
                did_create: None,
                will_create: None,
                did_rename: None,
                will_rename: Some(FileOperationRegistrationOptions {
                    filters: vec![
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**/*.rs"),
                                matches: Some(FileOperationPatternKind::File),
                                options: None,
                            },
                        },
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**"),
                                matches: Some(FileOperationPatternKind::Folder),
                                options: None,
                            },
                        },
                    ],
                }),
                did_delete: None,
                will_delete: None,
            }),
        }),
        call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
        semantic_tokens_provider: Some(
            SemanticTokensOptions {
                legend: SemanticTokensLegend {
                    token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
                    token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
                },

                full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
                range: Some(true),
                work_done_progress_options: Default::default(),
            }
            .into(),
        ),
        moniker_provider: None,
        experimental: Some(json!({
            "externalDocs": true,
            "hoverRange": true,
            "joinLines": true,
            "matchingBrace": true,
            "moveItems": true,
            "onEnter": true,
            "openCargoToml": true,
            "parentModule": true,
            "runnables": {
                "kinds": [ "cargo" ],
            },
            "ssr": true,
            "workspaceSymbolScopeKindFiltering": true,
        })),
    }
}

fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
    if completion_item_edit_resolve(client_caps) {
        Some(true)
    } else {
        tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
        None
    }
}

/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
    (|| {
        Some(
            caps.text_document
                .as_ref()?
                .completion
                .as_ref()?
                .completion_item
                .as_ref()?
                .resolve_support
                .as_ref()?
                .properties
                .iter()
                .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
        )
    })() == Some(true)
}

fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
    client_caps
        .text_document
        .as_ref()
        .and_then(|it| it.code_action.as_ref())
        .and_then(|it| it.code_action_literal_support.as_ref())
        .map_or(CodeActionProviderCapability::Simple(true), |_| {
            CodeActionProviderCapability::Options(CodeActionOptions {
                // Advertise support for all built-in CodeActionKinds.
                // Ideally we would base this off of the client capabilities
                // but the client is supposed to fall back gracefully for unknown values.
                code_action_kinds: Some(vec![
                    CodeActionKind::EMPTY,
                    CodeActionKind::QUICKFIX,
                    CodeActionKind::REFACTOR,
                    CodeActionKind::REFACTOR_EXTRACT,
                    CodeActionKind::REFACTOR_INLINE,
                    CodeActionKind::REFACTOR_REWRITE,
                ]),
                resolve_provider: Some(true),
                work_done_progress_options: Default::default(),
            })
        })
}
<commit_msg>Fix a typo in server_capabilities.experimental<commit_after>//! Advertises the capabilities of the LSP Server.
use lsp_types::{
    CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
    DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
    FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
    HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
    SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
    WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
    WorkspaceServerCapabilities,
};
use serde_json::json;

use crate::config::{Config, RustfmtConfig};
use crate::semantic_tokens;

pub fn server_capabilities(config: &Config) -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::INCREMENTAL),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default().into()),
        })),
        hover_provider: Some(HoverProviderCapability::Simple(true)),
        completion_provider: Some(CompletionOptions {
            resolve_provider: completions_resolve_provider(&config.caps),
            trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
            all_commit_characters: None,
            completion_item: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: Some(DeclarationCapability::Simple(true)),
        definition_provider: Some(OneOf::Left(true)),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(OneOf::Left(true)),
        document_highlight_provider: Some(OneOf::Left(true)),
        document_symbol_provider: Some(OneOf::Left(true)),
        workspace_symbol_provider: Some(OneOf::Left(true)),
        code_action_provider: Some(code_action_capabilities(&config.caps)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(OneOf::Left(true)),
        document_range_formatting_provider: match config.rustfmt() {
            RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
            _ => Some(OneOf::Left(false)),
        },
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(OneOf::Right(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        linked_editing_range_provider: None,
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: Some(WorkspaceServerCapabilities {
            workspace_folders: None,
            file_operations: Some(WorkspaceFileOperationsServerCapabilities {
                did_create: None,
                will_create: None,
                did_rename: None,
                will_rename: Some(FileOperationRegistrationOptions {
                    filters: vec![
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**/*.rs"),
                                matches: Some(FileOperationPatternKind::File),
                                options: None,
                            },
                        },
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**"),
                                matches: Some(FileOperationPatternKind::Folder),
                                options: None,
                            },
                        },
                    ],
                }),
                did_delete: None,
                will_delete: None,
            }),
        }),
        call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
        semantic_tokens_provider: Some(
            SemanticTokensOptions {
                legend: SemanticTokensLegend {
                    token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
                    token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
                },

                full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
                range: Some(true),
                work_done_progress_options: Default::default(),
            }
            .into(),
        ),
        moniker_provider: None,
        experimental: Some(json!({
            "externalDocs": true,
            "hoverRange": true,
            "joinLines": true,
            "matchingBrace": true,
            "moveItem": true,
            "onEnter": true,
            "openCargoToml": true,
            "parentModule": true,
            "runnables": {
                "kinds": [ "cargo" ],
            },
            "ssr": true,
            "workspaceSymbolScopeKindFiltering": true,
        })),
    }
}

fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
    if completion_item_edit_resolve(client_caps) {
        Some(true)
    } else {
        tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
        None
    }
}

/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
    (|| {
        Some(
            caps.text_document
                .as_ref()?
                .completion
                .as_ref()?
                .completion_item
                .as_ref()?
                .resolve_support
                .as_ref()?
                .properties
                .iter()
                .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
        )
    })() == Some(true)
}

fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
    client_caps
        .text_document
        .as_ref()
        .and_then(|it| it.code_action.as_ref())
        .and_then(|it| it.code_action_literal_support.as_ref())
        .map_or(CodeActionProviderCapability::Simple(true), |_| {
            CodeActionProviderCapability::Options(CodeActionOptions {
                // Advertise support for all built-in CodeActionKinds.
                // Ideally we would base this off of the client capabilities
                // but the client is supposed to fall back gracefully for unknown values.
                code_action_kinds: Some(vec![
                    CodeActionKind::EMPTY,
                    CodeActionKind::QUICKFIX,
                    CodeActionKind::REFACTOR,
                    CodeActionKind::REFACTOR_EXTRACT,
                    CodeActionKind::REFACTOR_INLINE,
                    CodeActionKind::REFACTOR_REWRITE,
                ]),
                resolve_provider: Some(true),
                work_done_progress_options: Default::default(),
            })
        })
}
<|endoftext|>


<commit_before>use query_builder::AsQuery;
use query_source::{JoinTo, QuerySource, Table};
use query_source::joins::{self, OnClauseWrapper};

#[doc(hidden)]
/// `JoinDsl` support trait to emulate associated type constructors
pub trait InternalJoinDsl<Rhs, Kind, On> {
    type Output: AsQuery;

    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output;
}

impl<T, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On> for T
where
    T: Table + AsQuery,
    T::Query: InternalJoinDsl<Rhs, Kind, On>,
{
    type Output = <T::Query as InternalJoinDsl<Rhs, Kind, On>>::Output;

    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
        self.as_query().join(rhs, kind, on)
    }
}

#[doc(hidden)]
/// `JoinDsl` support trait to emulate associated type constructors and grab
/// the known on clause from the associations API
pub trait JoinWithImplicitOnClause<Rhs, Kind> {
    type Output: AsQuery;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output;
}

impl<Lhs, Rhs, Kind> JoinWithImplicitOnClause<Rhs, Kind> for Lhs
where
    Lhs: JoinTo<Rhs>,
    Lhs: InternalJoinDsl<<Lhs as JoinTo<Rhs>>::FromClause, Kind, <Lhs as JoinTo<Rhs>>::OnClause>,
{
    type Output = <Lhs as InternalJoinDsl<Lhs::FromClause, Kind, Lhs::OnClause>>::Output;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output {
        let (from, on) = Lhs::join_target(rhs);
        self.join(from, kind, on)
    }
}

/// Methods allowing various joins between two or more tables.
///
/// Joining between two tables requires a [`#[belongs_to]`
/// association][associations] that defines the relationship.
///
/// You can join to as many tables as you'd like in a query, with the
/// restriction that no table can appear in the query more than once. The reason
/// for this restriction is that one of the appearances would require aliasing,
/// and we do not currently have a fleshed out story for dealing with table
/// aliases.
///
/// You may also need to call [`allow_tables_to_appear_in_same_query!`][] (particularly if
/// you see an unexpected error about `AppearsInFromClause`). See the
/// documentation for [`allow_tables_to_appear_in_same_query!`][] for details.
///
/// Diesel expects multi-table joins to be semantically grouped based on the
/// relationships. For example, `users.inner_join(posts.inner_join(comments))`
/// is not the same as `users.inner_join(posts).inner_join(comments)`. The first
/// would deserialize into `(User, (Post, Comment))` and generate the following
/// SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.post_id = posts.id
/// ```
///
/// While the second query would deserialize into `(User, Post, Comment)` and
/// generate the following SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.user_id = users.id
/// ```
///
/// [associations]: ../associations/index.html
/// [`allow_tables_to_appear_in_same_query!`]: ../macro.allow_tables_to_appear_in_same_query.html
pub trait JoinDsl: Sized {
    /// Join two tables using a SQL `INNER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn inner_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::Inner>,
    {
        self.join_with_implicit_on_clause(rhs, joins::Inner)
    }

    /// Join two tables using a SQL `LEFT OUTER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn left_outer_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.join_with_implicit_on_clause(rhs, joins::LeftOuter)
    }

    /// Alias for `left_outer_join`
    fn left_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.left_outer_join(rhs)
    }
}

impl<T: AsQuery> JoinDsl for T {}

pub trait JoinOnDsl: Sized {
    /// Specify the `ON` clause for a join statement. This will override
    /// any implicit `ON` clause that would come from `#[belongs_to]`
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         user_id -> Integer,
    /// #         title -> Text,
    /// #     }
    /// # }
    /// #
    /// # allow_tables_to_appear_in_same_query!(users, posts);
    /// #
    /// # fn main() {
    /// #     let connection = establish_connection();
    /// let data = users::table
    ///     .left_join(posts::table.on(
    ///         users::id.eq(posts::user_id).and(
    ///             posts::title.eq("My first post"))
    ///     ))
    ///     .select((users::name, posts::title.nullable()))
    ///     .load(&connection);
    /// let expected = vec![
    ///     ("Sean".to_string(), Some("My first post".to_string())),
    ///     ("Tess".to_string(), None),
    /// ];
    /// assert_eq!(Ok(expected), data);
    /// # }
    fn on<On>(self, on: On) -> OnClauseWrapper<Self, On> {
        OnClauseWrapper::new(self, on)
    }
}

impl<T: QuerySource> JoinOnDsl for T {}
<commit_msg>Update some out of date info on our Join DSL docs<commit_after>use query_builder::AsQuery;
use query_source::{JoinTo, QuerySource, Table};
use query_source::joins::{self, OnClauseWrapper};

#[doc(hidden)]
/// `JoinDsl` support trait to emulate associated type constructors
pub trait InternalJoinDsl<Rhs, Kind, On> {
    type Output: AsQuery;

    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output;
}

impl<T, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On> for T
where
    T: Table + AsQuery,
    T::Query: InternalJoinDsl<Rhs, Kind, On>,
{
    type Output = <T::Query as InternalJoinDsl<Rhs, Kind, On>>::Output;

    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
        self.as_query().join(rhs, kind, on)
    }
}

#[doc(hidden)]
/// `JoinDsl` support trait to emulate associated type constructors and grab
/// the known on clause from the associations API
pub trait JoinWithImplicitOnClause<Rhs, Kind> {
    type Output: AsQuery;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output;
}

impl<Lhs, Rhs, Kind> JoinWithImplicitOnClause<Rhs, Kind> for Lhs
where
    Lhs: JoinTo<Rhs>,
    Lhs: InternalJoinDsl<<Lhs as JoinTo<Rhs>>::FromClause, Kind, <Lhs as JoinTo<Rhs>>::OnClause>,
{
    type Output = <Lhs as InternalJoinDsl<Lhs::FromClause, Kind, Lhs::OnClause>>::Output;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output {
        let (from, on) = Lhs::join_target(rhs);
        self.join(from, kind, on)
    }
}

/// Methods allowing various joins between two or more tables.
///
/// If you have invoked [`joinable!`] for the two tables, you can pass that
/// table directly.  Otherwise you will need to use [`.on`] to specify the `ON`
/// clause.
///
/// [`joinable!`]: ../macro.joinable.html
/// [`.on`]: trait.JoinOnDsl.html#method.on
///
/// You can join to as many tables as you'd like in a query, with the
/// restriction that no table can appear in the query more than once. The reason
/// for this restriction is that one of the appearances would require aliasing,
/// and we do not currently have a fleshed out story for dealing with table
/// aliases.
///
/// You may also need to call [`allow_tables_to_appear_in_same_query!`][] (particularly if
/// you see an unexpected error about `AppearsInFromClause`). See the
/// documentation for [`allow_tables_to_appear_in_same_query!`][] for details.
///
/// Diesel expects multi-table joins to be semantically grouped based on the
/// relationships. For example, `users.inner_join(posts.inner_join(comments))`
/// is not the same as `users.inner_join(posts).inner_join(comments)`. The first
/// would deserialize into `(User, (Post, Comment))` and generate the following
/// SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.post_id = posts.id
/// ```
///
/// While the second query would deserialize into `(User, Post, Comment)` and
/// generate the following SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.user_id = users.id
/// ```
///
/// [associations]: ../associations/index.html
/// [`allow_tables_to_appear_in_same_query!`]: ../macro.allow_tables_to_appear_in_same_query.html
pub trait JoinDsl: Sized {
    /// Join two tables using a SQL `INNER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn inner_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::Inner>,
    {
        self.join_with_implicit_on_clause(rhs, joins::Inner)
    }

    /// Join two tables using a SQL `LEFT OUTER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn left_outer_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.join_with_implicit_on_clause(rhs, joins::LeftOuter)
    }

    /// Alias for `left_outer_join`
    fn left_join<Rhs>(self, rhs: Rhs) -> Self::Output
    where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.left_outer_join(rhs)
    }
}

impl<T: AsQuery> JoinDsl for T {}

pub trait JoinOnDsl: Sized {
    /// Specify the `ON` clause for a join statement. This will override
    /// any implicit `ON` clause that would come from [`joinable!`]
    ///
    /// [`joinable!`]: ../macro.joinable.html
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate diesel;
    /// # include!("../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # table! {
    /// #     posts {
    /// #         id -> Integer,
    /// #         user_id -> Integer,
    /// #         title -> Text,
    /// #     }
    /// # }
    /// #
    /// # allow_tables_to_appear_in_same_query!(users, posts);
    /// #
    /// # fn main() {
    /// #     let connection = establish_connection();
    /// let data = users::table
    ///     .left_join(posts::table.on(
    ///         users::id.eq(posts::user_id).and(
    ///             posts::title.eq("My first post"))
    ///     ))
    ///     .select((users::name, posts::title.nullable()))
    ///     .load(&connection);
    /// let expected = vec![
    ///     ("Sean".to_string(), Some("My first post".to_string())),
    ///     ("Tess".to_string(), None),
    /// ];
    /// assert_eq!(Ok(expected), data);
    /// # }
    fn on<On>(self, on: On) -> OnClauseWrapper<Self, On> {
        OnClauseWrapper::new(self, on)
    }
}

impl<T: QuerySource> JoinOnDsl for T {}
<|endoftext|>


<commit_before>use crate::mem;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::mutex::{self, Mutex};
use crate::sys::time::checked_dur2intervals;
use crate::time::Duration;

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

pub struct Condvar {
    condvar: AtomicU32,
}

pub type MovableCondvar = Condvar;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    pub const fn new() -> Condvar {
        Condvar { condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0) }
    }

    pub unsafe fn init(&mut self) {}

    pub unsafe fn notify_one(&self) {
        if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
            let ret = abi::condvar_signal(&self.condvar as *const AtomicU32 as *mut abi::condvar, abi::scope::PRIVATE, 1);
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to signal on condition variable");
        }
    }

    pub unsafe fn notify_all(&self) {
        if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
            let ret = abi::condvar_signal(
                &self.condvar as *const AtomicU32 as *mut abi::condvar,
                abi::scope::PRIVATE,
                abi::nthreads::MAX,
            );
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to broadcast on condition variable");
        }
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        let mutex = mutex::raw(mutex);
        assert_eq!(
            mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        // Call into the kernel to wait on the condition variable.
        let subscription = abi::subscription {
            type_: abi::eventtype::CONDVAR,
            union: abi::subscription_union {
                condvar: abi::subscription_condvar {
                    condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar,
                    condvar_scope: abi::scope::PRIVATE,
                    lock: mutex as *const AtomicU32 as *mut abi::lock,
                    lock_scope: abi::scope::PRIVATE,
                },
            },
            ..mem::zeroed()
        };
        let mut event: mem::MaybeUninit<abi::event> = mem::MaybeUninit::uninit();
        let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit();
        let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
        assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable");
        assert_eq!(
            event.assume_init().error,
            abi::errno::SUCCESS,
            "Failed to wait on condition variable"
        );
    }

    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
        let mutex = mutex::raw(mutex);
        assert_eq!(
            mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        // Call into the kernel to wait on the condition variable.
        let timeout =
            checked_dur2intervals(&dur).expect("overflow converting duration to nanoseconds");
        let subscriptions = [
            abi::subscription {
                type_: abi::eventtype::CONDVAR,
                union: abi::subscription_union {
                    condvar: abi::subscription_condvar {
                        condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar,
                        condvar_scope: abi::scope::PRIVATE,
                        lock: mutex as *const AtomicU32 as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            },
            abi::subscription {
                type_: abi::eventtype::CLOCK,
                union: abi::subscription_union {
                    clock: abi::subscription_clock {
                        clock_id: abi::clockid::MONOTONIC,
                        timeout,
                        ..mem::zeroed()
                    },
                },
                ..mem::zeroed()
            },
        ];
        let mut events: [mem::MaybeUninit<abi::event>; 2] = [mem::MaybeUninit::uninit(); 2];
        let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit();
        let ret = abi::poll(
            subscriptions.as_ptr(),
            mem::MaybeUninit::slice_as_mut_ptr(&mut events),
            2,
            nevents.as_mut_ptr(),
        );
        assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable");
        let nevents = nevents.assume_init();
        for i in 0..nevents {
            assert_eq!(
                events[i].assume_init().error,
                abi::errno::SUCCESS,
                "Failed to wait on condition variable"
            );
            if events[i].assume_init().type_ == abi::eventtype::CONDVAR {
                return true;
            }
        }
        false
    }

    pub unsafe fn destroy(&self) {
        assert_eq!(
            self.condvar.load(Ordering::Relaxed),
            abi::CONDVAR_HAS_NO_WAITERS.0,
            "Attempted to destroy a condition variable with blocked threads"
        );
    }
}
<commit_msg>Formatting.<commit_after>use crate::mem;
use crate::sync::atomic::{AtomicU32, Ordering};
use crate::sys::cloudabi::abi;
use crate::sys::mutex::{self, Mutex};
use crate::sys::time::checked_dur2intervals;
use crate::time::Duration;

extern "C" {
    #[thread_local]
    static __pthread_thread_id: abi::tid;
}

pub struct Condvar {
    condvar: AtomicU32,
}

pub type MovableCondvar = Condvar;

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    pub const fn new() -> Condvar {
        Condvar { condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0) }
    }

    pub unsafe fn init(&mut self) {}

    pub unsafe fn notify_one(&self) {
        if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
            let ret = abi::condvar_signal(
                &self.condvar as *const AtomicU32 as *mut abi::condvar,
                abi::scope::PRIVATE,
                1,
            );
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to signal on condition variable");
        }
    }

    pub unsafe fn notify_all(&self) {
        if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
            let ret = abi::condvar_signal(
                &self.condvar as *const AtomicU32 as *mut abi::condvar,
                abi::scope::PRIVATE,
                abi::nthreads::MAX,
            );
            assert_eq!(ret, abi::errno::SUCCESS, "Failed to broadcast on condition variable");
        }
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        let mutex = mutex::raw(mutex);
        assert_eq!(
            mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        // Call into the kernel to wait on the condition variable.
        let subscription = abi::subscription {
            type_: abi::eventtype::CONDVAR,
            union: abi::subscription_union {
                condvar: abi::subscription_condvar {
                    condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar,
                    condvar_scope: abi::scope::PRIVATE,
                    lock: mutex as *const AtomicU32 as *mut abi::lock,
                    lock_scope: abi::scope::PRIVATE,
                },
            },
            ..mem::zeroed()
        };
        let mut event: mem::MaybeUninit<abi::event> = mem::MaybeUninit::uninit();
        let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit();
        let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
        assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable");
        assert_eq!(
            event.assume_init().error,
            abi::errno::SUCCESS,
            "Failed to wait on condition variable"
        );
    }

    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
        let mutex = mutex::raw(mutex);
        assert_eq!(
            mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
            __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
            "This lock is not write-locked by this thread"
        );

        // Call into the kernel to wait on the condition variable.
        let timeout =
            checked_dur2intervals(&dur).expect("overflow converting duration to nanoseconds");
        let subscriptions = [
            abi::subscription {
                type_: abi::eventtype::CONDVAR,
                union: abi::subscription_union {
                    condvar: abi::subscription_condvar {
                        condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar,
                        condvar_scope: abi::scope::PRIVATE,
                        lock: mutex as *const AtomicU32 as *mut abi::lock,
                        lock_scope: abi::scope::PRIVATE,
                    },
                },
                ..mem::zeroed()
            },
            abi::subscription {
                type_: abi::eventtype::CLOCK,
                union: abi::subscription_union {
                    clock: abi::subscription_clock {
                        clock_id: abi::clockid::MONOTONIC,
                        timeout,
                        ..mem::zeroed()
                    },
                },
                ..mem::zeroed()
            },
        ];
        let mut events: [mem::MaybeUninit<abi::event>; 2] = [mem::MaybeUninit::uninit(); 2];
        let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit();
        let ret = abi::poll(
            subscriptions.as_ptr(),
            mem::MaybeUninit::slice_as_mut_ptr(&mut events),
            2,
            nevents.as_mut_ptr(),
        );
        assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable");
        let nevents = nevents.assume_init();
        for i in 0..nevents {
            assert_eq!(
                events[i].assume_init().error,
                abi::errno::SUCCESS,
                "Failed to wait on condition variable"
            );
            if events[i].assume_init().type_ == abi::eventtype::CONDVAR {
                return true;
            }
        }
        false
    }

    pub unsafe fn destroy(&self) {
        assert_eq!(
            self.condvar.load(Ordering::Relaxed),
            abi::CONDVAR_HAS_NO_WAITERS.0,
            "Attempted to destroy a condition variable with blocked threads"
        );
    }
}
<|endoftext|>


<commit_before>use crate::devices::uart_16550::SerialPort;
use crate::syscall::io::{Mmio, Pio};
use spin::Mutex;

pub static COM1: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3F8));
pub static COM2: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2F8));
pub static COM3: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3E8));
pub static COM4: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2E8));

#[cfg(feature = "lpss_debug")]
pub static LPSS: Mutex<Option<&'static mut SerialPort<Mmio<u32>>>> = Mutex::new(None);

pub unsafe fn init() {
    COM1.lock().init();
    COM2.lock().init();

    #[cfg(feature = "lpss_debug")]
    {
        // TODO: Make this configurable
        let address = crate::KERNEL_OFFSET + 0xFE032000;

        {
            use crate::paging::{ActivePageTable, Page, VirtualAddress, entry::EntryFlags};
            use crate::memory::{Frame, PhysicalAddress};

            let mut active_table = ActivePageTable::new();
            let page = Page::containing_address(VirtualAddress::new(address));
            let frame = Frame::containing_address(PhysicalAddress::new(address - crate::KERNEL_OFFSET));
            let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::WRITABLE | EntryFlags::NO_EXECUTE);
            result.flush(&mut active_table);
        }

        let lpss = SerialPort::<Mmio<u32>>::new(
            crate::KERNEL_OFFSET + 0xFE032000
        );
        lpss.init();

        *LPSS.lock() = Some(lpss);
    }
}
<commit_msg>Remove warning<commit_after>use crate::devices::uart_16550::SerialPort;
#[cfg(feature = "lpss_debug")]
use crate::syscall::io::Mmio;
use crate::syscall::io::Pio;
use spin::Mutex;

pub static COM1: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3F8));
pub static COM2: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2F8));
pub static COM3: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3E8));
pub static COM4: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2E8));

#[cfg(feature = "lpss_debug")]
pub static LPSS: Mutex<Option<&'static mut SerialPort<Mmio<u32>>>> = Mutex::new(None);

pub unsafe fn init() {
    COM1.lock().init();
    COM2.lock().init();

    #[cfg(feature = "lpss_debug")]
    {
        // TODO: Make this configurable
        let address = crate::KERNEL_OFFSET + 0xFE032000;

        {
            use crate::paging::{ActivePageTable, Page, VirtualAddress, entry::EntryFlags};
            use crate::memory::{Frame, PhysicalAddress};

            let mut active_table = ActivePageTable::new();
            let page = Page::containing_address(VirtualAddress::new(address));
            let frame = Frame::containing_address(PhysicalAddress::new(address - crate::KERNEL_OFFSET));
            let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::WRITABLE | EntryFlags::NO_EXECUTE);
            result.flush(&mut active_table);
        }

        let lpss = SerialPort::<Mmio<u32>>::new(
            crate::KERNEL_OFFSET + 0xFE032000
        );
        lpss.init();

        *LPSS.lock() = Some(lpss);
    }
}
<|endoftext|>