<commit_before>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::BuildError as TransportBuilderError,
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::time::{sleep, Duration};

use crate::adapters::secondary::elasticsearch::remote::{self, Error as ElasticsearchRemoteError};
use crate::domain::ports::secondary::remote::{Error as RemoteError, Remote};

pub const DOCKER_ES_VERSION: &str = "7.13.0";
pub const DOCKER_ES_TIMEOUT: u64 = 50;

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// This function takes the mutex related to the availability of the elasticsearch docker container,
// and then creates or reset the container depending on its availability.
// Finally, it tests if elasticsearch is available, before returning
// the mutex guard.
// TODO Document the usage of the mutexguard, how you should avoid running asserts
// while holding the mutex.
pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
    let guard = AVAILABLE
        .lock()
        .unwrap_or_else(|_| panic!("aborted because of other test"));

    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else {
        docker.cleanup().await?;
    }
    let is_available = docker.is_container_available().await?;
    if !is_available {
        return Err(Error::Misc {
            msg: format!("Cannot get docker {} available", docker.docker_image),
        });
    }
    let pool = remote::connection_test_pool()
        .await
        .context(ElasticsearchPoolCreation)?;
    let version_req = format!(">={}", DOCKER_ES_VERSION);
    let _client = pool
        .conn(DOCKER_ES_TIMEOUT, &version_req)
        .await
        .context(ElasticsearchConnection)?;

    Ok(guard)
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    DockerConnection { source: BollardError },

    #[snafu(display("Creation of elasticsearch pool: {}", source))]
    ElasticsearchPoolCreation { source: ElasticsearchRemoteError },

    #[snafu(display("Connection to elasticsearch: {}", source))]
    ElasticsearchConnection { source: RemoteError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch client error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerEngine { source: BollardError },

    #[snafu(display("error: {}", msg))]
    Misc { msg: String },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

// FIXME: Here we hardcode elasticsearch ports. These should be had from the ELASTICSEARCH_TEST_URL
impl Default for DockerWrapper {
    fn default() -> Self {
        let docker_image = format!(
            "docker.elastic.co/elasticsearch/elasticsearch:{}",
            DOCKER_ES_VERSION
        );
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image,
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                memory: Some(1_000_000_000), // limit docker container to use 1GB of ram
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(self.docker_image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerEngine)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerEngine)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerEngine)?;

        sleep(Duration::from_secs(30)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let pool = remote::connection_test_pool()
            .await
            .context(ElasticsearchPoolCreation)?;
        let version_req = format!(">={}", DOCKER_ES_VERSION);
        let storage = pool
            .conn(DOCKER_ES_TIMEOUT, &version_req)
            .await
            .context(ElasticsearchConnection)?;

        let _ = storage
            .client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(5)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<commit_msg>fix default conn parameters in docker<commit_after>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::BuildError as TransportBuilderError,
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::time::{sleep, Duration};

use crate::adapters::secondary::elasticsearch::{
    remote::{self, Error as ElasticsearchRemoteError},
    ES_DEFAULT_TIMEOUT, ES_DEFAULT_VERSION_REQ,
};
use crate::domain::ports::secondary::remote::{Error as RemoteError, Remote};

const DOCKER_ES_VERSION: &str = "7.13.0";

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// This function takes the mutex related to the availability of the elasticsearch docker container,
// and then creates or reset the container depending on its availability.
// Finally, it tests if elasticsearch is available, before returning
// the mutex guard.
// TODO Document the usage of the mutexguard, how you should avoid running asserts
// while holding the mutex.
pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
    let guard = AVAILABLE
        .lock()
        .unwrap_or_else(|_| panic!("aborted because of other test"));

    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else {
        docker.cleanup().await?;
    }
    let is_available = docker.is_container_available().await?;
    if !is_available {
        return Err(Error::Misc {
            msg: format!("Cannot get docker {} available", docker.docker_image),
        });
    }
    let pool = remote::connection_test_pool()
        .await
        .context(ElasticsearchPoolCreation)?;
    let _client = pool
        .conn(ES_DEFAULT_TIMEOUT, ES_DEFAULT_VERSION_REQ)
        .await
        .context(ElasticsearchConnection)?;

    Ok(guard)
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    DockerConnection { source: BollardError },

    #[snafu(display("Creation of elasticsearch pool: {}", source))]
    ElasticsearchPoolCreation { source: ElasticsearchRemoteError },

    #[snafu(display("Connection to elasticsearch: {}", source))]
    ElasticsearchConnection { source: RemoteError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch client error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerEngine { source: BollardError },

    #[snafu(display("error: {}", msg))]
    Misc { msg: String },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

// FIXME: Here we hardcode elasticsearch ports. These should be had from the ELASTICSEARCH_TEST_URL
impl Default for DockerWrapper {
    fn default() -> Self {
        let docker_image = format!(
            "docker.elastic.co/elasticsearch/elasticsearch:{}",
            DOCKER_ES_VERSION
        );
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image,
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                memory: Some(1_000_000_000), // limit docker container to use 1GB of ram
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(self.docker_image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerEngine)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerEngine)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerEngine)?;

        sleep(Duration::from_secs(30)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let pool = remote::connection_test_pool()
            .await
            .context(ElasticsearchPoolCreation)?;
        let storage = pool
            .conn(ES_DEFAULT_TIMEOUT, ES_DEFAULT_VERSION_REQ)
            .await
            .context(ElasticsearchConnection)?;

        let _ = storage
            .client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .request_timeout(storage.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(5)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<|endoftext|>


<commit_before>//! Naming well-known routines in the runtime library.

use ir::{
    types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, Opcode,
    Signature, Type,
};
use isa::{RegUnit, TargetIsa};
use settings::CallConv;
use std::fmt;
use std::str::FromStr;

/// The name of a runtime library routine.
///
/// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
/// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
/// the runtime library routine. This way, Cranelift doesn't have to know about the naming
/// convention in the embedding VM's runtime library.
///
/// This list is likely to grow over time.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LibCall {
    /// probe for stack overflow. These are emitted for functions which need
    /// when the `probestack_enabled` setting is true.
    Probestack,
    /// ceil.f32
    CeilF32,
    /// ceil.f64
    CeilF64,
    /// floor.f32
    FloorF32,
    /// floor.f64
    FloorF64,
    /// trunc.f32
    TruncF32,
    /// frunc.f64
    TruncF64,
    /// nearest.f32
    NearestF32,
    /// nearest.f64
    NearestF64,
}

const NAME: [&str; 9] = [
    "Probestack",
    "CeilF32",
    "CeilF64",
    "FloorF32",
    "FloorF64",
    "TruncF32",
    "TruncF64",
    "NearestF32",
    "NearestF64",
];

impl fmt::Display for LibCall {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(NAME[*self as usize])
    }
}

impl FromStr for LibCall {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "Probestack" => Ok(LibCall::Probestack),
            "CeilF32" => Ok(LibCall::CeilF32),
            "CeilF64" => Ok(LibCall::CeilF64),
            "FloorF32" => Ok(LibCall::FloorF32),
            "FloorF64" => Ok(LibCall::FloorF64),
            "TruncF32" => Ok(LibCall::TruncF32),
            "TruncF64" => Ok(LibCall::TruncF64),
            "NearestF32" => Ok(LibCall::NearestF32),
            "NearestF64" => Ok(LibCall::NearestF64),
            _ => Err(()),
        }
    }
}

impl LibCall {
    /// Get the well-known library call name to use as a replacement for an instruction with the
    /// given opcode and controlling type variable.
    ///
    /// Returns `None` if no well-known library routine name exists for that instruction.
    pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
        Some(match ctrl_type {
            types::F32 => match opcode {
                Opcode::Ceil => LibCall::CeilF32,
                Opcode::Floor => LibCall::FloorF32,
                Opcode::Trunc => LibCall::TruncF32,
                Opcode::Nearest => LibCall::NearestF32,
                _ => return None,
            },
            types::F64 => match opcode {
                Opcode::Ceil => LibCall::CeilF64,
                Opcode::Floor => LibCall::FloorF64,
                Opcode::Trunc => LibCall::TruncF64,
                Opcode::Nearest => LibCall::NearestF64,
                _ => return None,
            },
            _ => return None,
        })
    }
}

/// Get a function reference for `libcall` in `func`, following the signature
/// for `inst`.
///
/// If there is an existing reference, use it, otherwise make a new one.
pub fn get_libcall_funcref(
    libcall: LibCall,
    func: &mut Function,
    inst: Inst,
    isa: &TargetIsa,
) -> FuncRef {
    find_funcref(libcall, func).unwrap_or_else(|| make_funcref_for_inst(libcall, func, inst, isa))
}

/// Get a function reference for the probestack function in `func`.
///
/// If there is an existing reference, use it, otherwise make a new one.
pub fn get_probestack_funcref(
    func: &mut Function,
    reg_type: Type,
    arg_reg: RegUnit,
    isa: &TargetIsa,
) -> FuncRef {
    find_funcref(LibCall::Probestack, func)
        .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
}

/// Get the existing function reference for `libcall` in `func` if it exists.
fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
    // We're assuming that all libcall function decls are at the end.
    // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
    for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
        match func_data.name {
            ExternalName::LibCall(lc) => {
                if lc == libcall {
                    return Some(fref);
                }
            }
            _ => break,
        }
    }
    None
}

/// Create a funcref for `LibCall::Probestack`.
fn make_funcref_for_probestack(
    func: &mut Function,
    reg_type: Type,
    arg_reg: RegUnit,
    isa: &TargetIsa,
) -> FuncRef {
    let mut sig = Signature::new(CallConv::Probestack);
    let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
    sig.params.push(rax);
    if !isa.flags().probestack_func_adjusts_sp() {
        sig.returns.push(rax);
    }
    make_funcref(LibCall::Probestack, func, sig, isa)
}

/// Create a funcref for `libcall` with a signature matching `inst`.
fn make_funcref_for_inst(
    libcall: LibCall,
    func: &mut Function,
    inst: Inst,
    isa: &TargetIsa,
) -> FuncRef {
    // Start with a fast calling convention. We'll give the ISA a chance to change it.
    let mut sig = Signature::new(isa.flags().call_conv());
    for &v in func.dfg.inst_args(inst) {
        sig.params.push(AbiParam::new(func.dfg.value_type(v)));
    }
    for &v in func.dfg.inst_results(inst) {
        sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
    }

    make_funcref(libcall, func, sig, isa)
}

/// Create a funcref for `libcall`.
fn make_funcref(libcall: LibCall, func: &mut Function, sig: Signature, isa: &TargetIsa) -> FuncRef {
    let sigref = func.import_signature(sig);

    func.import_function(ExtFuncData {
        name: ExternalName::LibCall(libcall),
        signature: sigref,
        colocated: isa.flags().colocated_libcalls(),
    })
}

#[cfg(test)]
mod test {
    use super::*;
    use std::string::ToString;

    #[test]
    fn display() {
        assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
        assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
    }

    #[test]
    fn parsing() {
        assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
    }
}
<commit_msg>Replaced LibCall's fmt::Display implementation<commit_after>//! Naming well-known routines in the runtime library.

use ir::{
    types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, Opcode,
    Signature, Type,
};
use isa::{RegUnit, TargetIsa};
use settings::CallConv;
use std::fmt;
use std::str::FromStr;

/// The name of a runtime library routine.
///
/// Runtime library calls are generated for Cranelift IR instructions that don't have an equivalent
/// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to
/// the runtime library routine. This way, Cranelift doesn't have to know about the naming
/// convention in the embedding VM's runtime library.
///
/// This list is likely to grow over time.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LibCall {
    /// probe for stack overflow. These are emitted for functions which need
    /// when the `probestack_enabled` setting is true.
    Probestack,
    /// ceil.f32
    CeilF32,
    /// ceil.f64
    CeilF64,
    /// floor.f32
    FloorF32,
    /// floor.f64
    FloorF64,
    /// trunc.f32
    TruncF32,
    /// frunc.f64
    TruncF64,
    /// nearest.f32
    NearestF32,
    /// nearest.f64
    NearestF64,
}

impl fmt::Display for LibCall {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

impl FromStr for LibCall {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "Probestack" => Ok(LibCall::Probestack),
            "CeilF32" => Ok(LibCall::CeilF32),
            "CeilF64" => Ok(LibCall::CeilF64),
            "FloorF32" => Ok(LibCall::FloorF32),
            "FloorF64" => Ok(LibCall::FloorF64),
            "TruncF32" => Ok(LibCall::TruncF32),
            "TruncF64" => Ok(LibCall::TruncF64),
            "NearestF32" => Ok(LibCall::NearestF32),
            "NearestF64" => Ok(LibCall::NearestF64),
            _ => Err(()),
        }
    }
}

impl LibCall {
    /// Get the well-known library call name to use as a replacement for an instruction with the
    /// given opcode and controlling type variable.
    ///
    /// Returns `None` if no well-known library routine name exists for that instruction.
    pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
        Some(match ctrl_type {
            types::F32 => match opcode {
                Opcode::Ceil => LibCall::CeilF32,
                Opcode::Floor => LibCall::FloorF32,
                Opcode::Trunc => LibCall::TruncF32,
                Opcode::Nearest => LibCall::NearestF32,
                _ => return None,
            },
            types::F64 => match opcode {
                Opcode::Ceil => LibCall::CeilF64,
                Opcode::Floor => LibCall::FloorF64,
                Opcode::Trunc => LibCall::TruncF64,
                Opcode::Nearest => LibCall::NearestF64,
                _ => return None,
            },
            _ => return None,
        })
    }
}

/// Get a function reference for `libcall` in `func`, following the signature
/// for `inst`.
///
/// If there is an existing reference, use it, otherwise make a new one.
pub fn get_libcall_funcref(
    libcall: LibCall,
    func: &mut Function,
    inst: Inst,
    isa: &TargetIsa,
) -> FuncRef {
    find_funcref(libcall, func).unwrap_or_else(|| make_funcref_for_inst(libcall, func, inst, isa))
}

/// Get a function reference for the probestack function in `func`.
///
/// If there is an existing reference, use it, otherwise make a new one.
pub fn get_probestack_funcref(
    func: &mut Function,
    reg_type: Type,
    arg_reg: RegUnit,
    isa: &TargetIsa,
) -> FuncRef {
    find_funcref(LibCall::Probestack, func)
        .unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
}

/// Get the existing function reference for `libcall` in `func` if it exists.
fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
    // We're assuming that all libcall function decls are at the end.
    // If we get this wrong, worst case we'll have duplicate libcall decls which is harmless.
    for (fref, func_data) in func.dfg.ext_funcs.iter().rev() {
        match func_data.name {
            ExternalName::LibCall(lc) => {
                if lc == libcall {
                    return Some(fref);
                }
            }
            _ => break,
        }
    }
    None
}

/// Create a funcref for `LibCall::Probestack`.
fn make_funcref_for_probestack(
    func: &mut Function,
    reg_type: Type,
    arg_reg: RegUnit,
    isa: &TargetIsa,
) -> FuncRef {
    let mut sig = Signature::new(CallConv::Probestack);
    let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
    sig.params.push(rax);
    if !isa.flags().probestack_func_adjusts_sp() {
        sig.returns.push(rax);
    }
    make_funcref(LibCall::Probestack, func, sig, isa)
}

/// Create a funcref for `libcall` with a signature matching `inst`.
fn make_funcref_for_inst(
    libcall: LibCall,
    func: &mut Function,
    inst: Inst,
    isa: &TargetIsa,
) -> FuncRef {
    // Start with a fast calling convention. We'll give the ISA a chance to change it.
    let mut sig = Signature::new(isa.flags().call_conv());
    for &v in func.dfg.inst_args(inst) {
        sig.params.push(AbiParam::new(func.dfg.value_type(v)));
    }
    for &v in func.dfg.inst_results(inst) {
        sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
    }

    make_funcref(libcall, func, sig, isa)
}

/// Create a funcref for `libcall`.
fn make_funcref(libcall: LibCall, func: &mut Function, sig: Signature, isa: &TargetIsa) -> FuncRef {
    let sigref = func.import_signature(sig);

    func.import_function(ExtFuncData {
        name: ExternalName::LibCall(libcall),
        signature: sigref,
        colocated: isa.flags().colocated_libcalls(),
    })
}

#[cfg(test)]
mod test {
    use super::*;
    use std::string::ToString;

    #[test]
    fn display() {
        assert_eq!(LibCall::CeilF32.to_string(), "CeilF32");
        assert_eq!(LibCall::NearestF64.to_string(), "NearestF64");
    }

    #[test]
    fn parsing() {
        assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32));
    }
}
<|endoftext|>


<commit_before>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
use proc_macro2::Ident;
use serde::Deserialize;
use std::env;
use std::fs;
use std::path::PathBuf;

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                variants_from_header.push(enm);
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let ast_dump_bytes = match fs::read(&ast_dump_path) {
        Ok(ast_dump_bytes) => ast_dump_bytes,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref root: Node = match serde_json::from_slice(&ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(root, namespace, variants_from_header, None);

    for enm in variants_from_header {
        if enm.variants.is_empty() {
            let span = &enm.variants_from_header_attr;
            let mut msg = "failed to find any C++ definition of enum ".to_owned();
            for name in &enm.name.namespace {
                msg += &name.to_string();
                msg += "::";
            }
            msg += &enm.name.cxx.to_string();
            cx.error(span, msg);
        }
    }
}

fn traverse<'a>(
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => return,
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => return,
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant: unimplemented!(),
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}
<commit_msg>Adopt default variant name on name parse error<commit_after>use crate::syntax::attrs::OtherAttrs;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{Api, Doc, Enum, ForeignName, Pair, Variant};
use proc_macro2::Ident;
use quote::format_ident;
use serde::Deserialize;
use std::env;
use std::fs;
use std::path::PathBuf;

const CXX_CLANG_AST: &str = "CXX_CLANG_AST";

type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    Unknown,
}

#[derive(Deserialize)]
struct NamespaceDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumDecl {
    name: Option<String>,
}

#[derive(Deserialize)]
struct EnumConstantDecl {
    name: String,
}

pub fn load(cx: &mut Errors, apis: &mut [Api]) {
    let ref mut variants_from_header = Vec::new();
    for api in apis {
        if let Api::Enum(enm) = api {
            if enm.variants_from_header {
                variants_from_header.push(enm);
            }
        }
    }

    let span = match variants_from_header.get(0) {
        None => return,
        Some(enm) => enm.variants_from_header_attr.clone().unwrap(),
    };

    let ast_dump_path = match env::var_os(CXX_CLANG_AST) {
        Some(ast_dump_path) => PathBuf::from(ast_dump_path),
        None => {
            let msg = format!(
                "environment variable ${} has not been provided",
                CXX_CLANG_AST,
            );
            return cx.error(span, msg);
        }
    };

    let ast_dump_bytes = match fs::read(&ast_dump_path) {
        Ok(ast_dump_bytes) => ast_dump_bytes,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref root: Node = match serde_json::from_slice(&ast_dump_bytes) {
        Ok(root) => root,
        Err(error) => {
            let msg = format!("failed to read {}: {}", ast_dump_path.display(), error);
            return cx.error(span, msg);
        }
    };

    let ref mut namespace = Vec::new();
    traverse(cx, root, namespace, variants_from_header, None);

    for enm in variants_from_header {
        if enm.variants.is_empty() {
            let span = &enm.variants_from_header_attr;
            let mut msg = "failed to find any C++ definition of enum ".to_owned();
            for name in &enm.name.namespace {
                msg += &name.to_string();
                msg += "::";
            }
            msg += &enm.name.cxx.to_string();
            cx.error(span, msg);
        }
    }
}

fn traverse<'a>(
    cx: &mut Errors,
    node: &'a Node,
    namespace: &mut Vec<&'a str>,
    variants_from_header: &mut [&mut Enum],
    mut idx: Option<usize>,
) {
    match &node.kind {
        Clang::NamespaceDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                // Can ignore enums inside an anonymous namespace.
                None => return,
            };
            namespace.push(name);
            idx = None;
        }
        Clang::EnumDecl(decl) => {
            let name = match &decl.name {
                Some(name) => name,
                None => return,
            };
            idx = None;
            for (i, enm) in variants_from_header.iter().enumerate() {
                if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) {
                    idx = Some(i);
                    break;
                }
            }
            if idx.is_none() {
                return;
            }
        }
        Clang::EnumConstantDecl(decl) => {
            if let Some(idx) = idx {
                let enm = &mut *variants_from_header[idx];
                let span = enm
                    .variants_from_header_attr
                    .as_ref()
                    .unwrap()
                    .path
                    .get_ident()
                    .unwrap()
                    .span();
                let cxx_name = match ForeignName::parse(&decl.name, span) {
                    Ok(foreign_name) => foreign_name,
                    Err(_) => {
                        let span = &enm.variants_from_header_attr;
                        let msg = format!("unsupported C++ variant name: {}", decl.name);
                        return cx.error(span, msg);
                    }
                };
                let rust_name: Ident = match syn::parse_str(&decl.name) {
                    Ok(ident) => ident,
                    Err(_) => format_ident!("__Variant{}", enm.variants.len()),
                };
                enm.variants.push(Variant {
                    doc: Doc::new(),
                    attrs: OtherAttrs::none(),
                    name: Pair {
                        namespace: Namespace::ROOT,
                        cxx: cxx_name,
                        rust: rust_name,
                    },
                    discriminant: unimplemented!(),
                    expr: None,
                });
            }
        }
        _ => {}
    }
    for inner in &node.inner {
        traverse(cx, inner, namespace, variants_from_header, idx);
    }
    if let Clang::NamespaceDecl(_) = &node.kind {
        let _ = namespace.pop().unwrap();
    }
}
<|endoftext|>


<commit_before>mod mapper;

pub use self::mapper::PageMap;

use core::mem;
use x86_64::registers::control::{Cr0, Cr0Flags, Cr3};
use x86_64::registers::model_specific::{Efer, EferFlags};
use x86_64::structures::paging as pg;
use x86_64::structures::paging::PageTable;
use x86_64::structures::paging::PageTableFlags as Flags;
use x86_64::{PhysAddr, VirtAddr};

use d7alloc;

use crate::driver::vga_buffer::VGA_BUFFER_PHYSADDR;
use crate::interrupt::idt;
use crate::util::elf_parser::ELFData;

use super::constants::BOOT_TMP_PAGE_TABLE_P4;
use super::prelude::*;
use super::{Mapper, Page, PhysFrame};

pub unsafe fn enable_nxe() {
    Efer::update(|flags| flags.set(EferFlags::NO_EXECUTE_ENABLE, true))
}

pub unsafe fn enable_write_protection() {
    Cr0::update(|flags| {
        flags.set(Cr0Flags::WRITE_PROTECT, true);
    })
}

pub unsafe fn set_active_table(p4_addr: PhysAddr) {
    use x86_64::registers::control::{Cr3, Cr3Flags};
    Cr3::write(
        pg::PhysFrame::<pg::Size4KiB>::from_start_address(p4_addr).expect("Misaligned P4"),
        Cr3Flags::empty(),
    );
}

/// Remap kernel and other necessary memory areas
#[must_use]
pub unsafe fn init(elf_metadata: ELFData) -> PageMap {
    rprintln!("Remapping kernel...");

    // Create new page table
    let mut new_table = unsafe { PageMap::init(PT_VADDR, PT_PADDR, PT_VADDR) };

    // Kernel code and data segments
    new_table.identity_map_elf(PT_VADDR, elf_metadata);

    // Identity map IDT, GDT, and VGA text buffer
    let idt_frame = PhysFrame::containing_address(PhysAddr::new(idt::ADDRESS as u64));
    let vga_buffer_frame = PhysFrame::containing_address(VGA_BUFFER_PHYSADDR);

    assert_eq!(idt_frame, vga_buffer_frame);
    let lowmem_frame = idt_frame;

    unsafe {
        new_table
            .identity_map(
                PT_VADDR,
                lowmem_frame,
                Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
            )
            .ignore();
    }

    rprintln!("Switching to new table...");
    unsafe {
        new_table.activate();
    }
    rprintln!("Remapping done.");
    new_table
}
<commit_msg>Remove unnecessary import<commit_after>mod mapper;

pub use self::mapper::PageMap;

use core::mem;
use x86_64::registers::control::{Cr0, Cr0Flags, Cr3};
use x86_64::registers::model_specific::{Efer, EferFlags};
use x86_64::structures::paging as pg;
use x86_64::structures::paging::PageTable;
use x86_64::structures::paging::PageTableFlags as Flags;
use x86_64::{PhysAddr, VirtAddr};

use d7alloc;

use crate::driver::vga_buffer::VGA_BUFFER_PHYSADDR;
use crate::interrupt::idt;
use crate::util::elf_parser::ELFData;

use super::prelude::*;
use super::{Mapper, Page, PhysFrame};

pub unsafe fn enable_nxe() {
    Efer::update(|flags| flags.set(EferFlags::NO_EXECUTE_ENABLE, true))
}

pub unsafe fn enable_write_protection() {
    Cr0::update(|flags| {
        flags.set(Cr0Flags::WRITE_PROTECT, true);
    })
}

pub unsafe fn set_active_table(p4_addr: PhysAddr) {
    use x86_64::registers::control::{Cr3, Cr3Flags};
    Cr3::write(
        pg::PhysFrame::<pg::Size4KiB>::from_start_address(p4_addr).expect("Misaligned P4"),
        Cr3Flags::empty(),
    );
}

/// Remap kernel and other necessary memory areas
#[must_use]
pub unsafe fn init(elf_metadata: ELFData) -> PageMap {
    rprintln!("Remapping kernel...");

    // Create new page table
    let mut new_table = unsafe { PageMap::init(PT_VADDR, PT_PADDR, PT_VADDR) };

    // Kernel code and data segments
    new_table.identity_map_elf(PT_VADDR, elf_metadata);

    // Identity map IDT, GDT, and VGA text buffer
    let idt_frame = PhysFrame::containing_address(PhysAddr::new(idt::ADDRESS as u64));
    let vga_buffer_frame = PhysFrame::containing_address(VGA_BUFFER_PHYSADDR);

    assert_eq!(idt_frame, vga_buffer_frame);
    let lowmem_frame = idt_frame;

    unsafe {
        new_table
            .identity_map(
                PT_VADDR,
                lowmem_frame,
                Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
            )
            .ignore();
    }

    rprintln!("Switching to new table...");
    unsafe {
        new_table.activate();
    }
    rprintln!("Remapping done.");
    new_table
}
<|endoftext|>


<commit_before>use hir::HasSource;
use ide_db::assists::{AssistId, AssistKind};
use syntax::{
    ast::{self, edit::IndentLevel},
    AstNode, TextSize,
};

use crate::assist_context::{AssistContext, Assists};

// Assist: generate_enum_variant
//
// Adds a variant to an enum.
//
// ```
// enum Countries {
//     Ghana,
// }
//
// fn main() {
//     let country = Countries::Lesotho$0;
// }
// ```
// ->
// ```
// enum Countries {
//     Ghana,
//     Lesotho,
// }
//
// fn main() {
//     let country = Countries::Lesotho;
// }
// ```
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
    let path = path_expr.path()?;

    if ctx.sema.resolve_path(&path).is_some() {
        // No need to generate anything if the path resolves
        return None;
    }

    let name_ref = path.segment()?.name_ref()?;
    if name_ref.text().starts_with(char::is_lowercase) {
        // Don't suggest generating variant if the name starts with a lowercase letter
        return None;
    }

    if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
        ctx.sema.resolve_path(&path.qualifier()?)
    {
        let target = path.syntax().text_range();
        return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
    }

    None
}

fn add_variant_to_accumulator(
    acc: &mut Assists,
    ctx: &AssistContext,
    target: syntax::TextRange,
    adt: hir::Enum,
    name_ref: &ast::NameRef,
) -> Option<()> {
    let adt_ast = adt.source(ctx.db())?.original_ast_node(ctx.db())?.value;
    let enum_indent = IndentLevel::from_node(&adt_ast.syntax());

    let variant_list = adt_ast.variant_list()?;
    let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
    let empty_enum = variant_list.variants().next().is_none();

    acc.add(
        AssistId("generate_enum_variant", AssistKind::Generate),
        "Generate variant",
        target,
        |builder| {
            let text = format!(
                "{maybe_newline}{indent_1}{name},\n{enum_indent}",
                maybe_newline = if empty_enum { "\n" } else { "" },
                indent_1 = IndentLevel(1),
                name = name_ref,
                enum_indent = enum_indent
            );
            builder.insert(offset, text)
        },
    )
}

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

    use super::*;

    #[test]
    fn generate_basic_enum_variant_in_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {}
fn main() {
    Foo::Bar$0
}
",
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_non_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Baz$0
}
",
            r"
enum Foo {
    Bar,
    Baz,
}
fn main() {
    Foo::Baz
}
",
        )
    }

    #[test]
    fn not_applicable_for_existing_variant() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar$0
}
",
        )
    }

    #[test]
    fn not_applicable_for_lowercase() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::new$0
}
",
        )
    }

    #[test]
    fn indentation_level_is_correct() {
        check_assist(
            generate_enum_variant,
            r"
mod m {
    enum Foo {
        Bar,
    }
}
fn main() {
    m::Foo::Baz$0
}
",
            r"
mod m {
    enum Foo {
        Bar,
        Baz,
    }
}
fn main() {
    m::Foo::Baz
}
",
        )
    }
}
<commit_msg>Generate variant: insert code in file with enum definition<commit_after>use hir::{HasSource, InFile};
use ide_db::assists::{AssistId, AssistKind};
use syntax::{
    ast::{self, edit::IndentLevel},
    AstNode, TextSize,
};

use crate::assist_context::{AssistContext, Assists};

// Assist: generate_enum_variant
//
// Adds a variant to an enum.
//
// ```
// enum Countries {
//     Ghana,
// }
//
// fn main() {
//     let country = Countries::Lesotho$0;
// }
// ```
// ->
// ```
// enum Countries {
//     Ghana,
//     Lesotho,
// }
//
// fn main() {
//     let country = Countries::Lesotho;
// }
// ```
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
    let path = path_expr.path()?;

    if ctx.sema.resolve_path(&path).is_some() {
        // No need to generate anything if the path resolves
        return None;
    }

    let name_ref = path.segment()?.name_ref()?;
    if name_ref.text().starts_with(char::is_lowercase) {
        // Don't suggest generating variant if the name starts with a lowercase letter
        return None;
    }

    if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
        ctx.sema.resolve_path(&path.qualifier()?)
    {
        let target = path.syntax().text_range();
        return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
    }

    None
}

fn add_variant_to_accumulator(
    acc: &mut Assists,
    ctx: &AssistContext,
    target: syntax::TextRange,
    adt: hir::Enum,
    name_ref: &ast::NameRef,
) -> Option<()> {
    let db = ctx.db();
    let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?;
    let enum_indent = IndentLevel::from_node(&enum_node.syntax());

    let variant_list = enum_node.variant_list()?;
    let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
    let empty_enum = variant_list.variants().next().is_none();

    acc.add(
        AssistId("generate_enum_variant", AssistKind::Generate),
        "Generate variant",
        target,
        |builder| {
            builder.edit_file(file_id.original_file(db));
            let text = format!(
                "{maybe_newline}{indent_1}{name},\n{enum_indent}",
                maybe_newline = if empty_enum { "\n" } else { "" },
                indent_1 = IndentLevel(1),
                name = name_ref,
                enum_indent = enum_indent
            );
            builder.insert(offset, text)
        },
    )
}

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

    use super::*;

    #[test]
    fn generate_basic_enum_variant_in_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {}
fn main() {
    Foo::Bar$0
}
",
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_non_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Baz$0
}
",
            r"
enum Foo {
    Bar,
    Baz,
}
fn main() {
    Foo::Baz
}
",
        )
    }

    #[test]
    fn not_applicable_for_existing_variant() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar$0
}
",
        )
    }

    #[test]
    fn not_applicable_for_lowercase() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::new$0
}
",
        )
    }

    #[test]
    fn indentation_level_is_correct() {
        check_assist(
            generate_enum_variant,
            r"
mod m {
    enum Foo {
        Bar,
    }
}
fn main() {
    m::Foo::Baz$0
}
",
            r"
mod m {
    enum Foo {
        Bar,
        Baz,
    }
}
fn main() {
    m::Foo::Baz
}
",
        )
    }
}
<|endoftext|>


<commit_before>//! [GET /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-login)

use std::borrow::Cow;

use ruma_api::ruma_api;
use ruma_identifiers::MxcUri;
use ruma_serde::{JsonObject, StringEnum};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value as JsonValue;

use crate::PrivOwnedStr;

ruma_api! {
    metadata: {
        description: "Gets the homeserver's supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.",
        method: GET,
        name: "get_login_types",
        path: "/_matrix/client/r0/login",
        rate_limited: true,
        authentication: None,
    }

    #[derive(Default)]
    request: {}

    response: {
        /// The homeserver's supported login types.
        pub flows: Vec<LoginType>,
    }

    error: crate::Error
}

impl Request {
    /// Creates an empty `Request`.
    pub fn new() -> Self {
        Self {}
    }
}

impl Response {
    /// Creates a new `Response` with the given login types.
    pub fn new(flows: Vec<LoginType>) -> Self {
        Self { flows }
    }
}

/// An authentication mechanism.
#[derive(Clone, Debug, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(untagged)]
pub enum LoginType {
    /// A password is supplied to authenticate.
    Password(PasswordLoginType),

    /// Token-based login.
    Token(TokenLoginType),

    /// SSO-based login.
    Sso(SsoLoginType),

    /// Custom login type.
    #[doc(hidden)]
    _Custom(CustomLoginType),
}

impl LoginType {
    /// Creates a new `LoginType` with the given `login_type` string and data.
    ///
    /// Prefer to use the public variants of `LoginType` where possible; this constructor is meant
    /// be used for unsupported login types only and does not allow setting arbitrary data for
    /// supported ones.
    pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result<Self> {
        fn from_json_object<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
            serde_json::from_value(JsonValue::Object(obj))
        }

        Ok(match login_type {
            "m.login.password" => Self::Password(from_json_object(data)?),
            "m.login.token" => Self::Token(from_json_object(data)?),
            "m.login.sso" => Self::Sso(from_json_object(data)?),
            _ => Self::_Custom(CustomLoginType { type_: login_type.to_owned(), data }),
        })
    }

    /// Returns a reference to the `login_type` string.
    pub fn login_type(&self) -> &str {
        match self {
            Self::Password(_) => "m.login.password",
            Self::Token(_) => "m.login.token",
            Self::Sso(_) => "m.login.sso",
            Self::_Custom(c) => &c.type_,
        }
    }

    /// Returns the associated data.
    ///
    /// Prefer to use the public variants of `LoginType` where possible; this method is meant to
    /// be used for unsupported login types only.
    pub fn data(&self) -> Cow<'_, JsonObject> {
        fn serialize<T: Serialize>(obj: &T) -> JsonObject {
            match serde_json::to_value(obj).expect("login type serialization to succeed") {
                JsonValue::Object(obj) => obj,
                _ => panic!("all login types must serialize to objects"),
            }
        }

        match self {
            Self::Password(d) => Cow::Owned(serialize(d)),
            Self::Token(d) => Cow::Owned(serialize(d)),
            Self::Sso(d) => Cow::Owned(serialize(d)),
            Self::_Custom(c) => Cow::Borrowed(&c.data),
        }
    }
}

/// The payload for password login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.password")]
pub struct PasswordLoginType {}

impl PasswordLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self {}
    }
}

/// The payload for token-based login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.token")]
pub struct TokenLoginType {}

impl TokenLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self {}
    }
}

/// The payload for SSO login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.sso")]
pub struct SsoLoginType {
    /// The identity provider choices.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub identity_providers: Vec<IdentityProvider>,
}

impl SsoLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self::default()
    }
}

/// An SSO login identity provider.
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IdentityProvider {
    /// The ID of the provider.
    pub id: String,

    /// The display name of the provider.
    pub name: String,

    /// The icon for the provider.
    pub icon: Option<Box<MxcUri>>,

    /// The brand identifier for the provider.
    pub brand: Option<IdentityProviderBrand>,
}

impl IdentityProvider {
    /// Creates an `IdentityProvider` with the given `id` and `name`.
    pub fn new(id: String, name: String) -> Self {
        Self { id, name, icon: None, brand: None }
    }
}

/// An SSO login identity provider brand identifier.
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum IdentityProviderBrand {
    /// The [Apple] brand.
    ///
    /// [Apple]: https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
    #[ruma_enum(rename = "org.matrix.apple")]
    Apple,

    /// The [Facebook](https://developers.facebook.com/docs/facebook-login/web/login-button/) brand.
    #[ruma_enum(rename = "org.matrix.facebook")]
    Facebook,

    /// The [GitHub](https://github.com/logos) brand.
    #[ruma_enum(rename = "org.matrix.github")]
    GitHub,

    /// The [GitLab](https://about.gitlab.com/press/press-kit/) brand.
    #[ruma_enum(rename = "org.matrix.gitlab")]
    GitLab,

    /// The [Google](https://developers.google.com/identity/branding-guidelines) brand.
    #[ruma_enum(rename = "org.matrix.google")]
    Google,

    /// The [Twitter] brand.
    ///
    /// [Twitter]: https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1
    #[ruma_enum(rename = "org.matrix.twitter")]
    Twitter,

    /// A custom brand.
    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}

/// A custom login payload.
#[doc(hidden)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[allow(clippy::exhaustive_structs)]
pub struct CustomLoginType {
    /// A custom type
    ///
    /// This field is named `type_` instead of `type` because the latter is a reserved
    /// keyword in Rust.
    #[serde(rename = "type")]
    pub type_: String,

    /// Remaining type content
    #[serde(flatten)]
    pub data: JsonObject,
}

mod login_type_serde;

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use serde::{Deserialize, Serialize};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{
        CustomLoginType, IdentityProvider, IdentityProviderBrand, LoginType, PasswordLoginType,
        SsoLoginType, TokenLoginType,
    };

    #[derive(Debug, Deserialize, Serialize)]
    struct Wrapper {
        pub flows: Vec<LoginType>,
    }

    #[test]
    fn deserialize_password_login_type() {
        assert_matches!(
            from_json_value::<Wrapper>(json!({
                "flows": [
                    { "type": "m.login.password" }
                ],
            })),
            Ok(Wrapper { flows })
            if flows.len() == 1
                && matches!(flows[0], LoginType::Password(PasswordLoginType {}))
        );
    }

    #[test]
    fn deserialize_custom_login_type() {
        assert_matches!(
            from_json_value::<Wrapper>(json!({
                "flows": [
                    {
                        "type": "io.ruma.custom",
                        "color": "green",
                    }
                ],
            })),
            Ok(Wrapper { flows })
            if flows.len() == 1
                && matches!(
                    &flows[0],
                    LoginType::_Custom(CustomLoginType { type_, data })
                        if type_ == "io.ruma.custom"
                        && data == json!({ "color": "green" }).as_object().unwrap()
                )
        );
    }

    #[test]
    fn deserialize_sso_login_type() {
        let mut wrapper = from_json_value::<Wrapper>(json!({
            "flows": [
                {
                    "type": "m.login.sso",
                    "identity_providers": [
                        {
                            "id": "oidc-gitlab",
                            "name": "GitLab",
                            "icon": "mxc://localhost/gitlab-icon",
                            "brand": "org.matrix.gitlab"
                        },
                        {
                            "id": "custom",
                            "name": "Custom",
                        }
                    ]
                }
            ],
        }))
        .unwrap();

        let flow = wrapper.flows.pop();
        assert_matches!(wrapper.flows.as_slice(), []);

        let mut identity_providers = match flow {
            Some(LoginType::Sso(SsoLoginType { identity_providers })) => identity_providers,
            _ => panic!("unexpected enum variant: {:?}", flow),
        };

        let provider = identity_providers.pop();
        assert_matches!(
            provider,
            Some(IdentityProvider {
                id,
                name,
                icon: None,
                brand: None,
            }) if id == "custom"
                && name == "Custom"
        );

        let provider = identity_providers.pop();
        assert_matches!(
            provider,
            Some(IdentityProvider {
                id,
                name,
                icon: Some(icon),
                brand: Some(IdentityProviderBrand::GitLab),
            }) if id == "oidc-gitlab"
                && name == "GitLab"
                && icon == "mxc://localhost/gitlab-icon"
        );
    }

    #[test]
    fn serialize_sso_login_type() {
        let wrapper = to_json_value(Wrapper {
            flows: vec![
                LoginType::Token(TokenLoginType {}),
                LoginType::Sso(SsoLoginType {
                    identity_providers: vec![IdentityProvider {
                        id: "oidc-github".into(),
                        name: "GitHub".into(),
                        icon: Some("mxc://localhost/github-icon".into()),
                        brand: Some(IdentityProviderBrand::GitHub),
                    }],
                }),
            ],
        })
        .unwrap();

        assert_eq!(
            wrapper,
            json!({
                "flows": [
                    {
                        "type": "m.login.token"
                    },
                    {
                        "type": "m.login.sso",
                        "identity_providers": [
                            {
                                "id": "oidc-github",
                                "name": "GitHub",
                                "icon": "mxc://localhost/github-icon",
                                "brand": "org.matrix.github"
                            },
                        ]
                    }
                ],
            })
        );
    }
}
<commit_msg>client-api: Fix serialized form of IdentityProviderBrand and improve its docs<commit_after>//! [GET /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-login)

use std::borrow::Cow;

use ruma_api::ruma_api;
use ruma_identifiers::MxcUri;
use ruma_serde::{JsonObject, StringEnum};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value as JsonValue;

use crate::PrivOwnedStr;

ruma_api! {
    metadata: {
        description: "Gets the homeserver's supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.",
        method: GET,
        name: "get_login_types",
        path: "/_matrix/client/r0/login",
        rate_limited: true,
        authentication: None,
    }

    #[derive(Default)]
    request: {}

    response: {
        /// The homeserver's supported login types.
        pub flows: Vec<LoginType>,
    }

    error: crate::Error
}

impl Request {
    /// Creates an empty `Request`.
    pub fn new() -> Self {
        Self {}
    }
}

impl Response {
    /// Creates a new `Response` with the given login types.
    pub fn new(flows: Vec<LoginType>) -> Self {
        Self { flows }
    }
}

/// An authentication mechanism.
#[derive(Clone, Debug, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(untagged)]
pub enum LoginType {
    /// A password is supplied to authenticate.
    Password(PasswordLoginType),

    /// Token-based login.
    Token(TokenLoginType),

    /// SSO-based login.
    Sso(SsoLoginType),

    /// Custom login type.
    #[doc(hidden)]
    _Custom(CustomLoginType),
}

impl LoginType {
    /// Creates a new `LoginType` with the given `login_type` string and data.
    ///
    /// Prefer to use the public variants of `LoginType` where possible; this constructor is meant
    /// be used for unsupported login types only and does not allow setting arbitrary data for
    /// supported ones.
    pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result<Self> {
        fn from_json_object<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
            serde_json::from_value(JsonValue::Object(obj))
        }

        Ok(match login_type {
            "m.login.password" => Self::Password(from_json_object(data)?),
            "m.login.token" => Self::Token(from_json_object(data)?),
            "m.login.sso" => Self::Sso(from_json_object(data)?),
            _ => Self::_Custom(CustomLoginType { type_: login_type.to_owned(), data }),
        })
    }

    /// Returns a reference to the `login_type` string.
    pub fn login_type(&self) -> &str {
        match self {
            Self::Password(_) => "m.login.password",
            Self::Token(_) => "m.login.token",
            Self::Sso(_) => "m.login.sso",
            Self::_Custom(c) => &c.type_,
        }
    }

    /// Returns the associated data.
    ///
    /// Prefer to use the public variants of `LoginType` where possible; this method is meant to
    /// be used for unsupported login types only.
    pub fn data(&self) -> Cow<'_, JsonObject> {
        fn serialize<T: Serialize>(obj: &T) -> JsonObject {
            match serde_json::to_value(obj).expect("login type serialization to succeed") {
                JsonValue::Object(obj) => obj,
                _ => panic!("all login types must serialize to objects"),
            }
        }

        match self {
            Self::Password(d) => Cow::Owned(serialize(d)),
            Self::Token(d) => Cow::Owned(serialize(d)),
            Self::Sso(d) => Cow::Owned(serialize(d)),
            Self::_Custom(c) => Cow::Borrowed(&c.data),
        }
    }
}

/// The payload for password login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.password")]
pub struct PasswordLoginType {}

impl PasswordLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self {}
    }
}

/// The payload for token-based login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.token")]
pub struct TokenLoginType {}

impl TokenLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self {}
    }
}

/// The payload for SSO login.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type", rename = "m.login.sso")]
pub struct SsoLoginType {
    /// The identity provider choices.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub identity_providers: Vec<IdentityProvider>,
}

impl SsoLoginType {
    /// Creates a new `PasswordLoginType`.
    pub fn new() -> Self {
        Self::default()
    }
}

/// An SSO login identity provider.
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IdentityProvider {
    /// The ID of the provider.
    pub id: String,

    /// The display name of the provider.
    pub name: String,

    /// The icon for the provider.
    pub icon: Option<Box<MxcUri>>,

    /// The brand identifier for the provider.
    pub brand: Option<IdentityProviderBrand>,
}

impl IdentityProvider {
    /// Creates an `IdentityProvider` with the given `id` and `name`.
    pub fn new(id: String, name: String) -> Self {
        Self { id, name, icon: None, brand: None }
    }
}

/// An SSO login identity provider brand identifier.
///
/// The predefined ones can be found in the matrix-doc repo in a [separate document][matrix-doc].
/// To use a custom brand string, simply use `IdentityProviderBrand::from("custom-brand")` or
/// `"custom-brand".into()` (if the type is known from the surrounding context).
///
/// [matrix-doc]: https://github.com/matrix-org/matrix-doc/blob/v1.1/informal/idp-brands.md
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum IdentityProviderBrand {
    /// The [Apple] brand.
    ///
    /// [Apple]: https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
    #[ruma_enum(rename = "apple")]
    Apple,

    /// The [Facebook](https://developers.facebook.com/docs/facebook-login/web/login-button/) brand.
    #[ruma_enum(rename = "facebook")]
    Facebook,

    /// The [GitHub](https://github.com/logos) brand.
    #[ruma_enum(rename = "github")]
    GitHub,

    /// The [GitLab](https://about.gitlab.com/press/press-kit/) brand.
    #[ruma_enum(rename = "gitlab")]
    GitLab,

    /// The [Google](https://developers.google.com/identity/branding-guidelines) brand.
    #[ruma_enum(rename = "google")]
    Google,

    /// The [Twitter] brand.
    ///
    /// [Twitter]: https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1
    #[ruma_enum(rename = "twitter")]
    Twitter,

    /// A custom brand.
    #[doc(hidden)]
    _Custom(PrivOwnedStr),
}

/// A custom login payload.
#[doc(hidden)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[allow(clippy::exhaustive_structs)]
pub struct CustomLoginType {
    /// A custom type
    ///
    /// This field is named `type_` instead of `type` because the latter is a reserved
    /// keyword in Rust.
    #[serde(rename = "type")]
    pub type_: String,

    /// Remaining type content
    #[serde(flatten)]
    pub data: JsonObject,
}

mod login_type_serde;

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use serde::{Deserialize, Serialize};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{
        CustomLoginType, IdentityProvider, IdentityProviderBrand, LoginType, PasswordLoginType,
        SsoLoginType, TokenLoginType,
    };

    #[derive(Debug, Deserialize, Serialize)]
    struct Wrapper {
        pub flows: Vec<LoginType>,
    }

    #[test]
    fn deserialize_password_login_type() {
        assert_matches!(
            from_json_value::<Wrapper>(json!({
                "flows": [
                    { "type": "m.login.password" }
                ],
            })),
            Ok(Wrapper { flows })
            if flows.len() == 1
                && matches!(flows[0], LoginType::Password(PasswordLoginType {}))
        );
    }

    #[test]
    fn deserialize_custom_login_type() {
        assert_matches!(
            from_json_value::<Wrapper>(json!({
                "flows": [
                    {
                        "type": "io.ruma.custom",
                        "color": "green",
                    }
                ],
            })),
            Ok(Wrapper { flows })
            if flows.len() == 1
                && matches!(
                    &flows[0],
                    LoginType::_Custom(CustomLoginType { type_, data })
                        if type_ == "io.ruma.custom"
                        && data == json!({ "color": "green" }).as_object().unwrap()
                )
        );
    }

    #[test]
    fn deserialize_sso_login_type() {
        let mut wrapper = from_json_value::<Wrapper>(json!({
            "flows": [
                {
                    "type": "m.login.sso",
                    "identity_providers": [
                        {
                            "id": "oidc-gitlab",
                            "name": "GitLab",
                            "icon": "mxc://localhost/gitlab-icon",
                            "brand": "org.matrix.gitlab"
                        },
                        {
                            "id": "custom",
                            "name": "Custom",
                        }
                    ]
                }
            ],
        }))
        .unwrap();

        let flow = wrapper.flows.pop();
        assert_matches!(wrapper.flows.as_slice(), []);

        let mut identity_providers = match flow {
            Some(LoginType::Sso(SsoLoginType { identity_providers })) => identity_providers,
            _ => panic!("unexpected enum variant: {:?}", flow),
        };

        let provider = identity_providers.pop();
        assert_matches!(
            provider,
            Some(IdentityProvider {
                id,
                name,
                icon: None,
                brand: None,
            }) if id == "custom"
                && name == "Custom"
        );

        let provider = identity_providers.pop();
        assert_matches!(
            provider,
            Some(IdentityProvider {
                id,
                name,
                icon: Some(icon),
                brand: Some(IdentityProviderBrand::GitLab),
            }) if id == "oidc-gitlab"
                && name == "GitLab"
                && icon == "mxc://localhost/gitlab-icon"
        );
    }

    #[test]
    fn serialize_sso_login_type() {
        let wrapper = to_json_value(Wrapper {
            flows: vec![
                LoginType::Token(TokenLoginType {}),
                LoginType::Sso(SsoLoginType {
                    identity_providers: vec![IdentityProvider {
                        id: "oidc-github".into(),
                        name: "GitHub".into(),
                        icon: Some("mxc://localhost/github-icon".into()),
                        brand: Some(IdentityProviderBrand::GitHub),
                    }],
                }),
            ],
        })
        .unwrap();

        assert_eq!(
            wrapper,
            json!({
                "flows": [
                    {
                        "type": "m.login.token"
                    },
                    {
                        "type": "m.login.sso",
                        "identity_providers": [
                            {
                                "id": "oidc-github",
                                "name": "GitHub",
                                "icon": "mxc://localhost/github-icon",
                                "brand": "org.matrix.github"
                            },
                        ]
                    }
                ],
            })
        );
    }
}
<|endoftext|>


<commit_before>//! Implementation of the Xenstore protocol

use core::result;
use core::str::FromStr;

use ffi::CString;

use cell::GlobalCell;

use self::imp::XenStoreImpl;

use hal::xen::defs::{XenstoreInterface, EvtchnPort};

pub use self::transaction::Transaction;

static STORE: GlobalCell<XenStoreImpl> = GlobalCell::new();

mod imp;
mod transaction;

/// Result of an operation within the Xen Store
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
/// Error code that can be returned by a Xen Store operation
pub enum Error {
    /// Unknown error happened
    Unknown,
    /// A type conversion error
    Conversion,
    /// Invalid argument
    Inval,
    /// Permission denied
    Acces,
    /// File exists
    Exist,
    /// Is a directory
    Isdir,
    /// No such file or directory
    Noent,
    /// Out of memory
    Nomem,
    /// No space left on device
    Nospc,
    /// I/O error
    Io,
    /// Directory not empty
    Notempty,
    /// Function not implemented
    Nosys,
    /// Read-only filesystem
    Rofs,
    /// Device or resource is busy
    Busy,
    /// Try again
    Again,
    /// Transport endpoint is already connected
    Isconn,
}

/// Main interface of the Xen Store
pub struct XenStore;

impl XenStore {
    #[doc(hidden)]
    pub unsafe fn init(interface: *mut XenstoreInterface, port: EvtchnPort) {
        STORE.set(XenStoreImpl::new(interface, port));
    }

    #[doc(hidden)]
    pub unsafe fn init_event() {
        STORE.as_mut().init_event();
    }

    /// Start a new transaction within the Xen Store
    pub fn start_transaction<'a>() -> Result<Transaction<'a>> {
        Transaction::new(STORE.as_mut())
    }

    /// Read a value from the xen store and convert it
    pub fn read_value<T: FromStr>(path: CString) -> Result<T> {
        let mut t = try!(Self::start_transaction());

        let val = try!(t.read(path));

        try!(t.end());

        T::from_str(val.as_str()).or_else(|_| Err(Error::Conversion))
    }
}
<commit_msg>xen/store: impl From<Error> for ()<commit_after>//! Implementation of the Xenstore protocol

use core::result;
use core::str::FromStr;

use ffi::CString;

use cell::GlobalCell;

use self::imp::XenStoreImpl;

use hal::xen::defs::{XenstoreInterface, EvtchnPort};

pub use self::transaction::Transaction;

static STORE: GlobalCell<XenStoreImpl> = GlobalCell::new();

mod imp;
mod transaction;

/// Result of an operation within the Xen Store
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
/// Error code that can be returned by a Xen Store operation
pub enum Error {
    /// Unknown error happened
    Unknown,
    /// A type conversion error
    Conversion,
    /// Invalid argument
    Inval,
    /// Permission denied
    Acces,
    /// File exists
    Exist,
    /// Is a directory
    Isdir,
    /// No such file or directory
    Noent,
    /// Out of memory
    Nomem,
    /// No space left on device
    Nospc,
    /// I/O error
    Io,
    /// Directory not empty
    Notempty,
    /// Function not implemented
    Nosys,
    /// Read-only filesystem
    Rofs,
    /// Device or resource is busy
    Busy,
    /// Try again
    Again,
    /// Transport endpoint is already connected
    Isconn,
}

/// Useful for try!
impl From<Error> for () {
    #[inline]
    fn from(_: Error) -> Self {
        ()
    }
}

/// Main interface of the Xen Store
pub struct XenStore;

impl XenStore {
    #[doc(hidden)]
    pub unsafe fn init(interface: *mut XenstoreInterface, port: EvtchnPort) {
        STORE.set(XenStoreImpl::new(interface, port));
    }

    #[doc(hidden)]
    pub unsafe fn init_event() {
        STORE.as_mut().init_event();
    }

    /// Start a new transaction within the Xen Store
    pub fn start_transaction<'a>() -> Result<Transaction<'a>> {
        Transaction::new(STORE.as_mut())
    }

    /// Read a value from the xen store and convert it
    pub fn read_value<T: FromStr>(path: CString) -> Result<T> {
        let mut t = try!(Self::start_transaction());

        let val = try!(t.read(path));

        try!(t.end());

        T::from_str(val.as_str()).or_else(|_| Err(Error::Conversion))
    }
}
<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   return it;
}

/* SNIP
    let mut res: Vec<Vec<(int, int)>> =
        // rotations
        iterate(piece, |rot| rot.iter().map(|&(y, x)| (x + y, -y)).collect())
        .take(if all {6} else {3})
        // mirror
        .flat_map(|cur_piece| {
            iterate(cur_piece, |mir| mir.iter().map(|&(y, x)| (x, y)).collect())
            .take(2)
        }).collect();
 */

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0 = interned_to_ident(labels); // FIXME: parse this field

   println!("Defining items in the CSV provider...");

   // FIXME: Why is this fn necessary?
   let define_my_csv_row = |cx0 : &mut ExtCtxt| {
      return quote_item!(cx0,
         pub struct $MyCsvRow {
            pub $col0: String,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<commit_msg>another step up<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0 = interned_to_ident(labels); // FIXME: parse this field

   println!("Defining items in the CSV provider...");

   // FIXME: Why is this fn necessary?
   let define_my_csv_row = |cx0 : &mut ExtCtxt| {
      return quote_item!(cx0,
         pub struct $MyCsvRow {
            pub $col0: String,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<|endoftext|>