<commit_before>use eyre::{bail, Result};
use serde::Serialize;
use serde_json::json;
use std::collections::HashMap;
use stencila_schema::{
    Article, BlockContent, CodeBlock, CodeChunk, CodeChunkCaption, CreativeWorkAuthors,
    CreativeWorkTitle, ImageObject, Node, Person,
};

use crate::methods::encode::txt::ToTxt;

use super::md::ToMd;

/// Encode a `Node` to a Jupyter Notebook.
///
/// Note that the order of properties in various JSON objects is
/// consistent with Jupyter and other tools. Also, the JSON is
/// pretty printed with a one space indentation.
pub fn encode(node: &Node) -> Result<String> {
    let article = match node {
        Node::Article(article) => article,
        _ => bail!("Only able to encode an `Article` as a Jupyter Notebook`"),
    };

    let notebook = json!({
        "cells": encode_content(&article.content),
        "metadata": encode_metadata(article),
        "nbformat": 4,
        "nbformat_minor": 5
    });

    let buffer = Vec::new();
    let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
    let mut serializer = serde_json::Serializer::with_formatter(buffer, formatter);
    notebook.serialize(&mut serializer).unwrap();
    let json = String::from_utf8(serializer.into_inner())?;

    Ok(json)
}

/// Encode `Article` properties, other than `content` as a JSON object
///
/// The nbformat-v4 schema requires that `title` be a string
/// and that `authors` be an array of objects with a `name` string
/// property (and optional additional properties). This function
/// conforms to those requirements.
fn encode_metadata(article: &Article) -> serde_json::Value {
    let mut metadata = serde_json::to_value(article).expect("Should serialize");
    let object = metadata.as_object_mut().expect("Should be object");
    object.remove("type");
    object.remove("content");

    if let Some(title) = article.title.as_deref() {
        let title = match title {
            CreativeWorkTitle::String(string) => string.clone(),
            CreativeWorkTitle::VecInlineContent(inlines) => inlines.to_txt(),
        };
        object.insert("title".to_string(), json!(title));
    }

    if let Some(authors) = article.authors.as_ref() {
        let authors = authors
            .iter()
            .map(|author| {
                let name = match author {
                    CreativeWorkAuthors::Person(person) => {
                        let Person {
                            name,
                            given_names,
                            family_names,
                            ..
                        } = person;
                        if let Some(name) = name.as_deref() {
                            name.clone()
                        } else {
                            [
                                &given_names
                                    .as_ref()
                                    .map(|names| names.join(" "))
                                    .unwrap_or_default(),
                                " ",
                                &family_names
                                    .as_ref()
                                    .map(|names| names.join(" "))
                                    .unwrap_or_default(),
                            ]
                            .concat()
                            .trim()
                            .to_string()
                        }
                    }
                    CreativeWorkAuthors::Organization(org) => {
                        if let Some(name) = org.name.as_deref() {
                            name.clone()
                        } else {
                            "Unnamed".to_string()
                        }
                    }
                };
                let mut value = serde_json::to_value(author).expect("Should serialize");
                let object = value.as_object_mut().expect("Should be object");
                object.insert("name".to_string(), json!(name));
                value
            })
            .collect::<Vec<serde_json::Value>>();
        object.insert("authors".to_string(), json!(authors));
    }

    metadata
}

/// Encode a `Article`'s content as a vector of Jupyter cells
fn encode_content(content: &Option<Vec<BlockContent>>) -> Vec<serde_json::Value> {
    let blocks = if let Some(blocks) = content {
        blocks
    } else {
        return Vec::new();
    };

    let mut cells = Vec::with_capacity(blocks.len());
    let mut content = Vec::new();
    for block in blocks {
        match block {
            BlockContent::CodeChunk(chunk) => {
                if !content.is_empty() {
                    cells.push(encode_markdown(&content));
                    content.clear()
                }
                cells.push(encode_chunk(&chunk));
            }
            _ => content.push(block.clone()),
        }
    }
    if !content.is_empty() {
        cells.push(encode_markdown(&content))
    }

    cells
}

/// Encode a `CodeChunk` to a Jupyter code cell
fn encode_chunk(chunk: &CodeChunk) -> serde_json::Value {
    let mut metadata: HashMap<String, serde_json::Value> = HashMap::new();

    if let Some(id) = chunk.id.as_ref() {
        metadata.insert("id".to_string(), json!(*id));
    }

    if let Some(label) = chunk.label.as_ref() {
        metadata.insert("label".to_string(), json!(*label));
    }

    if let Some(caption) = chunk.caption.as_deref() {
        let caption = match caption {
            CodeChunkCaption::String(string) => string.clone(),
            CodeChunkCaption::VecBlockContent(blocks) => blocks.to_md(),
        };
        metadata.insert("caption".to_string(), json!(caption));
    }

    if !chunk.programming_language.is_empty() {
        metadata.insert(
            "language_info".to_string(),
            json!({
                "name": chunk.programming_language
            }),
        );
    }

    let source = encode_multiline_string(&chunk.text);

    let outputs = if let Some(outputs) = &chunk.outputs {
        encode_outputs(outputs)
    } else {
        Vec::new()
    };

    json!({
        "cell_type": "code",
        "source" : source,
        "outputs": outputs,
        "metadata": metadata
    })
}

/// Encode the `outputs` of a Stencila `CodeChunk` to an array of Jupyter `Output`s.
///
/// Note that the Stencila document model does not differentiate among different sources
/// of outputs e.g. `stdout` from a `print` statement versus a `string` from a `execute_result`.
/// So, we don't try to revert to the source that may have been in the `ipynb` originally.
/// Instead, consistent with decoding, preformatted text in a code block goes to a stream
/// output.
fn encode_outputs(nodes: &[Node]) -> Vec<serde_json::Value> {
    nodes
        .iter()
        .map(|node| match node {
            Node::CodeBlock(CodeBlock { text, .. }) => encode_stream(text),
            Node::ImageObject(image) => encode_display_data(image),
            _ => encode_execute_result(node),
        })
        .collect()
}

/// Encode a `String` as a Jupyter `Stream`.
fn encode_stream(text: &String) -> serde_json::Value {
    json!({
        "output_type": "stream",
        "name": "stdout",
        "text": text,
    })
}

/// Encode an `ImageObject` as a Jupyter `DisplayData`.
fn encode_display_data(image: &ImageObject) -> serde_json::Value {
    let url = &image.content_url;
    let data = if let Some(data) = url.strip_prefix("data:") {
        let parts = data.split(";base64,").collect::<Vec<&str>>();
        let mime_type = parts.get(0).map(|str| str.to_string()).unwrap_or_default();
        let data = parts.get(1);
        let data = if mime_type.starts_with("application/vnd.plotly")
            || mime_type.starts_with("application/vnd.vega")
        {
            data.and_then(|data| base64::decode(data).ok())
                .and_then(|bytes| serde_json::from_slice(&bytes).ok())
                .unwrap_or_else(|| json!({}))
        } else {
            json!(data)
        };
        json!({ mime_type: data })
    } else {
        json!({})
    };
    json!({
        "output_type": "display_data",
        "data": data,
        "metadata": {},
    })
}

/// Encode a `Node` as a Jupyter `ExecuteResult`.
fn encode_execute_result(node: &Node) -> serde_json::Value {
    let text = node.to_txt();
    let data = json!({ "text/plain": encode_multiline_string(&text) });
    json!({
        "output_type": "execute_result",
        "data": data,
        "metadata": {},
    })
}

/// Encode a vector of `BlockContent` to a Jupyter Markdown cell
fn encode_markdown(blocks: &[BlockContent]) -> serde_json::Value {
    let md = blocks.to_md();
    json!({
        "cell_type": "markdown",
        "source" : encode_multiline_string(&md),
        "metadata": {}
    })
}

/// Encode a `String` to a Jupyter multiline string
fn encode_multiline_string(string: &str) -> Vec<&str> {
    string.split_inclusive("\n").collect()
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{methods::decode::ipynb::decode, utils::tests::snapshot_fixtures};
    use insta::assert_json_snapshot;

    #[test]
    fn ipynb_articles() {
        snapshot_fixtures("articles/*.ipynb", |_path, content| {
            let decoded = decode(&content).unwrap();
            let encoded = encode(&decoded).unwrap();
            let json = serde_json::from_str::<serde_json::Value>(&encoded).unwrap();
            assert_json_snapshot!(json);
        });
    }
}
<commit_msg>chore(IPYNB): Linting<commit_after>use eyre::{bail, Result};
use serde::Serialize;
use serde_json::json;
use std::collections::HashMap;
use stencila_schema::{
    Article, BlockContent, CodeBlock, CodeChunk, CodeChunkCaption, CreativeWorkAuthors,
    CreativeWorkTitle, ImageObject, Node, Person,
};

use crate::methods::encode::txt::ToTxt;

use super::md::ToMd;

/// Encode a `Node` to a Jupyter Notebook.
///
/// Note that the order of properties in various JSON objects is
/// consistent with Jupyter and other tools. Also, the JSON is
/// pretty printed with a one space indentation.
pub fn encode(node: &Node) -> Result<String> {
    let article = match node {
        Node::Article(article) => article,
        _ => bail!("Only able to encode an `Article` as a Jupyter Notebook`"),
    };

    let notebook = json!({
        "cells": encode_content(&article.content),
        "metadata": encode_metadata(article),
        "nbformat": 4,
        "nbformat_minor": 5
    });

    let buffer = Vec::new();
    let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
    let mut serializer = serde_json::Serializer::with_formatter(buffer, formatter);
    notebook.serialize(&mut serializer).unwrap();
    let json = String::from_utf8(serializer.into_inner())?;

    Ok(json)
}

/// Encode `Article` properties, other than `content` as a JSON object
///
/// The nbformat-v4 schema requires that `title` be a string
/// and that `authors` be an array of objects with a `name` string
/// property (and optional additional properties). This function
/// conforms to those requirements.
fn encode_metadata(article: &Article) -> serde_json::Value {
    let mut metadata = serde_json::to_value(article).expect("Should serialize");
    let object = metadata.as_object_mut().expect("Should be object");
    object.remove("type");
    object.remove("content");

    if let Some(title) = article.title.as_deref() {
        let title = match title {
            CreativeWorkTitle::String(string) => string.clone(),
            CreativeWorkTitle::VecInlineContent(inlines) => inlines.to_txt(),
        };
        object.insert("title".to_string(), json!(title));
    }

    if let Some(authors) = article.authors.as_ref() {
        let authors = authors
            .iter()
            .map(|author| {
                let name = match author {
                    CreativeWorkAuthors::Person(person) => {
                        let Person {
                            name,
                            given_names,
                            family_names,
                            ..
                        } = person;
                        if let Some(name) = name.as_deref() {
                            name.clone()
                        } else {
                            [
                                &given_names
                                    .as_ref()
                                    .map(|names| names.join(" "))
                                    .unwrap_or_default(),
                                " ",
                                &family_names
                                    .as_ref()
                                    .map(|names| names.join(" "))
                                    .unwrap_or_default(),
                            ]
                            .concat()
                            .trim()
                            .to_string()
                        }
                    }
                    CreativeWorkAuthors::Organization(org) => {
                        if let Some(name) = org.name.as_deref() {
                            name.clone()
                        } else {
                            "Unnamed".to_string()
                        }
                    }
                };
                let mut value = serde_json::to_value(author).expect("Should serialize");
                let object = value.as_object_mut().expect("Should be object");
                object.insert("name".to_string(), json!(name));
                value
            })
            .collect::<Vec<serde_json::Value>>();
        object.insert("authors".to_string(), json!(authors));
    }

    metadata
}

/// Encode a `Article`'s content as a vector of Jupyter cells
fn encode_content(content: &Option<Vec<BlockContent>>) -> Vec<serde_json::Value> {
    let blocks = if let Some(blocks) = content {
        blocks
    } else {
        return Vec::new();
    };

    let mut cells = Vec::with_capacity(blocks.len());
    let mut content = Vec::new();
    for block in blocks {
        match block {
            BlockContent::CodeChunk(chunk) => {
                if !content.is_empty() {
                    cells.push(encode_markdown(&content));
                    content.clear()
                }
                cells.push(encode_chunk(chunk));
            }
            _ => content.push(block.clone()),
        }
    }
    if !content.is_empty() {
        cells.push(encode_markdown(&content))
    }

    cells
}

/// Encode a `CodeChunk` to a Jupyter code cell
fn encode_chunk(chunk: &CodeChunk) -> serde_json::Value {
    let mut metadata: HashMap<String, serde_json::Value> = HashMap::new();

    if let Some(id) = chunk.id.as_ref() {
        metadata.insert("id".to_string(), json!(*id));
    }

    if let Some(label) = chunk.label.as_ref() {
        metadata.insert("label".to_string(), json!(*label));
    }

    if let Some(caption) = chunk.caption.as_deref() {
        let caption = match caption {
            CodeChunkCaption::String(string) => string.clone(),
            CodeChunkCaption::VecBlockContent(blocks) => blocks.to_md(),
        };
        metadata.insert("caption".to_string(), json!(caption));
    }

    if !chunk.programming_language.is_empty() {
        metadata.insert(
            "language_info".to_string(),
            json!({
                "name": chunk.programming_language
            }),
        );
    }

    let source = encode_multiline_string(&chunk.text);

    let outputs = if let Some(outputs) = &chunk.outputs {
        encode_outputs(outputs)
    } else {
        Vec::new()
    };

    json!({
        "cell_type": "code",
        "source" : source,
        "outputs": outputs,
        "metadata": metadata
    })
}

/// Encode the `outputs` of a Stencila `CodeChunk` to an array of Jupyter `Output`s.
///
/// Note that the Stencila document model does not differentiate among different sources
/// of outputs e.g. `stdout` from a `print` statement versus a `string` from a `execute_result`.
/// So, we don't try to revert to the source that may have been in the `ipynb` originally.
/// Instead, consistent with decoding, preformatted text in a code block goes to a stream
/// output.
fn encode_outputs(nodes: &[Node]) -> Vec<serde_json::Value> {
    nodes
        .iter()
        .map(|node| match node {
            Node::CodeBlock(CodeBlock { text, .. }) => encode_stream(text),
            Node::ImageObject(image) => encode_display_data(image),
            _ => encode_execute_result(node),
        })
        .collect()
}

/// Encode a `String` as a Jupyter `Stream`.
fn encode_stream(text: &str) -> serde_json::Value {
    json!({
        "output_type": "stream",
        "name": "stdout",
        "text": text,
    })
}

/// Encode an `ImageObject` as a Jupyter `DisplayData`.
fn encode_display_data(image: &ImageObject) -> serde_json::Value {
    let url = &image.content_url;
    let data = if let Some(data) = url.strip_prefix("data:") {
        let parts = data.split(";base64,").collect::<Vec<&str>>();
        let mime_type = parts.get(0).map(|str| str.to_string()).unwrap_or_default();
        let data = parts.get(1);
        let data = if mime_type.starts_with("application/vnd.plotly")
            || mime_type.starts_with("application/vnd.vega")
        {
            data.and_then(|data| base64::decode(data).ok())
                .and_then(|bytes| serde_json::from_slice(&bytes).ok())
                .unwrap_or_else(|| json!({}))
        } else {
            json!(data)
        };
        json!({ mime_type: data })
    } else {
        json!({})
    };
    json!({
        "output_type": "display_data",
        "data": data,
        "metadata": {},
    })
}

/// Encode a `Node` as a Jupyter `ExecuteResult`.
fn encode_execute_result(node: &Node) -> serde_json::Value {
    let text = node.to_txt();
    let data = json!({ "text/plain": encode_multiline_string(&text) });
    json!({
        "output_type": "execute_result",
        "data": data,
        "metadata": {},
    })
}

/// Encode a vector of `BlockContent` to a Jupyter Markdown cell
fn encode_markdown(blocks: &[BlockContent]) -> serde_json::Value {
    let md = blocks.to_md();
    json!({
        "cell_type": "markdown",
        "source" : encode_multiline_string(&md),
        "metadata": {}
    })
}

/// Encode a `String` to a Jupyter multiline string
fn encode_multiline_string(string: &str) -> Vec<&str> {
    string.split_inclusive("\n").collect()
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{methods::decode::ipynb::decode, utils::tests::snapshot_fixtures};
    use insta::assert_json_snapshot;

    #[test]
    fn ipynb_articles() {
        snapshot_fixtures("articles/*.ipynb", |_path, content| {
            let decoded = decode(&content).unwrap();
            let encoded = encode(&decoded).unwrap();
            let json = serde_json::from_str::<serde_json::Value>(&encoded).unwrap();
            assert_json_snapshot!(json);
        });
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate pkg_config;
extern crate wayland_scanner;

use std::{env, io, fs};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    meson();
    let protocol_header_path =
        generate_protocol_headers().expect("Could not generate header files for wayland protocols");
    let target_dir = env::var("OUT_DIR").expect("$OUT_DIR not set!");
    let mut builder = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_type(r"^xkb_.*$")
        .whitelisted_type(r"^XKB_.*$")
        .whitelisted_function(r"^_?pixman_.*$")
        .whitelisted_function(r"^_?wlr_.*$")
        .whitelisted_function(r"^xkb_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        // NOTE Necessary because they use the out directory to put
        // pragma information on what features are available in a header file
        // titled "config.h"
        .clang_arg(format!("-I{}{}", target_dir, "/include/"))
        .clang_arg(format!("-I{}", protocol_header_path.to_str().unwrap()))
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL");
    if cfg!(feature = "unstable-features") {
        builder = builder.clang_arg("-DWLR_USE_UNSTABLE");
    }
    let generated = builder.generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=xcb-composite");
    println!("cargo:rustc-link-lib=dylib=xcb-xfixes");
    println!("cargo:rustc-link-lib=dylib=xcb-image");
    println!("cargo:rustc-link-lib=dylib=xcb-render");
    println!("cargo:rustc-link-lib=dylib=xcb-shm");
    println!("cargo:rustc-link-lib=dylib=xcb-icccm");
    println!("cargo:rustc-link-lib=dylib=xcb-xkb");
    println!("cargo:rustc-link-lib=dylib=xcb-errors");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    link_optional_libs();

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    generated.write_to_file("src/gen.rs").unwrap();

    generate_protocols();
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path =
        PathBuf::from(env::var("OUT_DIR").expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path.to_str()
                                   .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/",
                 build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/",
                 build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_render");
        // wayland protocols
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    if Path::new("wlroots").exists() {
        meson::build("wlroots", build_path_str);
    } else {
        panic!("The `wlroots` submodule does not exist");
    }
}

/// Gets the unstable and stable protocols in /usr/share-wayland-protocols and
/// generates server headers for them.
///
/// The path to the folder with the generated headers is returned. It will
/// have two directories, `stable`, and `unstable`.
fn generate_protocol_headers() -> io::Result<PathBuf> {
    let output_dir_str = env::var("OUT_DIR").unwrap();
    let out_path: PathBuf = format!("{}/wayland-protocols", output_dir_str).into();
    fs::create_dir(&out_path).ok();
    let protocols = fs::read_dir("/usr/share/wayland-protocols/stable")?
        .chain(fs::read_dir("/usr/share/wayland-protocols/unstable")?);
    for entry in protocols {
        let entry = entry?;
        for entry in fs::read_dir(entry.path())? {
            let entry = entry?;
            let path = entry.path();
            let mut filename = entry.file_name().into_string().unwrap();
            if filename.ends_with(".xml") {
                let new_length = filename.len() - 4;
                filename.truncate(new_length);
            }
            filename.push_str("-protocol");
            Command::new("wayland-scanner").arg("server-header")
                                           .arg(path.clone())
                                           .arg(format!("{}/{}.h",
                                                        out_path.to_str().unwrap(),
                                                        filename))
                                           .status()
                                           .unwrap();
        }
    }
    Ok(out_path)
}

fn generate_protocols() {
    let output_dir_str = env::var("OUT_DIR").unwrap();

    let output_dir = Path::new(&output_dir_str);

    let protocols = &[("./wlroots/protocol/server-decoration.xml", "server_decoration")];

    for protocol in protocols {
        wayland_scanner::generate_code(protocol.0,
                                       output_dir.join(format!("{}_server_api.rs", protocol.1)),
                                       wayland_scanner::Side::Server);
        wayland_scanner::generate_code(protocol.0,
                                       output_dir.join(format!("{}_client_api.rs", protocol.1)),
                                       wayland_scanner::Side::Client);
        wayland_scanner::generate_interfaces(protocol.0,
                                             output_dir.join(format!("{}_interfaces.rs",
                                                                     protocol.1)));
    }
}

fn link_optional_libs() {
    if cfg!(feature = "libcap") && pkg_config::probe_library("libcap").is_ok() {
        println!("cargo:rustc-link-lib=dylib=cap");
    }
    if cfg!(feature = "systemd") && pkg_config::probe_library("libsystemd").is_ok() {
        println!("cargo:rustc-link-lib=dylib=systemd");
    }
    if cfg!(feature = "elogind") && pkg_config::probe_library("elogind").is_ok() {
        println!("cargo:rustc-link-lib=dylib=elogind");
    }
}
<commit_msg>Use pkg-config to find the share dir for wayland-protocols<commit_after>extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate pkg_config;
extern crate wayland_scanner;

use std::{env, io, fs};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    meson();
    let protocol_header_path =
        generate_protocol_headers().expect("Could not generate header files for wayland protocols");
    let target_dir = env::var("OUT_DIR").expect("$OUT_DIR not set!");
    let mut builder = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_type(r"^xkb_.*$")
        .whitelisted_type(r"^XKB_.*$")
        .whitelisted_function(r"^_?pixman_.*$")
        .whitelisted_function(r"^_?wlr_.*$")
        .whitelisted_function(r"^xkb_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        // NOTE Necessary because they use the out directory to put
        // pragma information on what features are available in a header file
        // titled "config.h"
        .clang_arg(format!("-I{}{}", target_dir, "/include/"))
        .clang_arg(format!("-I{}", protocol_header_path.to_str().unwrap()))
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL");
    if cfg!(feature = "unstable-features") {
        builder = builder.clang_arg("-DWLR_USE_UNSTABLE");
    }
    let generated = builder.generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=xcb-composite");
    println!("cargo:rustc-link-lib=dylib=xcb-xfixes");
    println!("cargo:rustc-link-lib=dylib=xcb-image");
    println!("cargo:rustc-link-lib=dylib=xcb-render");
    println!("cargo:rustc-link-lib=dylib=xcb-shm");
    println!("cargo:rustc-link-lib=dylib=xcb-icccm");
    println!("cargo:rustc-link-lib=dylib=xcb-xkb");
    println!("cargo:rustc-link-lib=dylib=xcb-errors");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    link_optional_libs();

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    generated.write_to_file("src/gen.rs").unwrap();

    generate_protocols();
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path =
        PathBuf::from(env::var("OUT_DIR").expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path.to_str()
                                   .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/",
                 build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/",
                 build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_render");
        // wayland protocols
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    if Path::new("wlroots").exists() {
        meson::build("wlroots", build_path_str);
    } else {
        panic!("The `wlroots` submodule does not exist");
    }
}

/// Gets the unstable and stable protocols in /usr/share-wayland-protocols and
/// generates server headers for them.
///
/// The path to the folder with the generated headers is returned. It will
/// have two directories, `stable`, and `unstable`.
fn generate_protocol_headers() -> io::Result<PathBuf> {
    let output_dir_str = env::var("OUT_DIR").unwrap();
    let out_path: PathBuf = format!("{}/wayland-protocols", output_dir_str).into();
    fs::create_dir(&out_path).ok();
    let protocols_prefix = pkg_config::get_variable("wayland-protocols", "prefix").unwrap();
    let protocols = fs::read_dir(format!("{}/share/wayland-protocols/stable", protocols_prefix))?
        .chain(fs::read_dir(format!("{}/share/wayland-protocols/unstable", protocols_prefix))?);
    for entry in protocols {
        let entry = entry?;
        for entry in fs::read_dir(entry.path())? {
            let entry = entry?;
            let path = entry.path();
            let mut filename = entry.file_name().into_string().unwrap();
            if filename.ends_with(".xml") {
                let new_length = filename.len() - 4;
                filename.truncate(new_length);
            }
            filename.push_str("-protocol");
            Command::new("wayland-scanner").arg("server-header")
                                           .arg(path.clone())
                                           .arg(format!("{}/{}.h",
                                                        out_path.to_str().unwrap(),
                                                        filename))
                                           .status()
                                           .unwrap();
        }
    }
    Ok(out_path)
}

fn generate_protocols() {
    let output_dir_str = env::var("OUT_DIR").unwrap();

    let output_dir = Path::new(&output_dir_str);

    let protocols = &[("./wlroots/protocol/server-decoration.xml", "server_decoration")];

    for protocol in protocols {
        wayland_scanner::generate_code(protocol.0,
                                       output_dir.join(format!("{}_server_api.rs", protocol.1)),
                                       wayland_scanner::Side::Server);
        wayland_scanner::generate_code(protocol.0,
                                       output_dir.join(format!("{}_client_api.rs", protocol.1)),
                                       wayland_scanner::Side::Client);
        wayland_scanner::generate_interfaces(protocol.0,
                                             output_dir.join(format!("{}_interfaces.rs",
                                                                     protocol.1)));
    }
}

fn link_optional_libs() {
    if cfg!(feature = "libcap") && pkg_config::probe_library("libcap").is_ok() {
        println!("cargo:rustc-link-lib=dylib=cap");
    }
    if cfg!(feature = "systemd") && pkg_config::probe_library("libsystemd").is_ok() {
        println!("cargo:rustc-link-lib=dylib=systemd");
    }
    if cfg!(feature = "elogind") && pkg_config::probe_library("elogind").is_ok() {
        println!("cargo:rustc-link-lib=dylib=elogind");
    }
}
<|endoftext|>


<commit_before>use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
    def::{DefKind, Res},
    Item, ItemKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::BytePos;

declare_clippy_lint! {
    /// **What it does:** Checks for `use Enum::*`.
    ///
    /// **Why is this bad?** It is usually better style to use the prefixed name of
    /// an enumeration variant, rather than importing variants.
    ///
    /// **Known problems:** Old-style enumerations that prefix the variants are
    /// still around.
    ///
    /// **Example:**
    /// ```rust
    /// use std::cmp::Ordering::*;
    /// ```
    pub ENUM_GLOB_USE,
    pedantic,
    "use items that import all variants of an enum"
}

declare_clippy_lint! {
    /// **What it does:** Checks for wildcard imports `use _::*`.
    ///
    /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
    /// you try to import something through a wildcard, that already has been imported by name from
    /// a different source:
    ///
    /// ```rust,ignore
    /// use crate1::foo; // Imports a function named foo
    /// use crate2::*; // Has a function named foo
    ///
    /// foo(); // Calls crate1::foo
    /// ```
    ///
    /// This can lead to confusing error messages at best and to unexpected behavior at worst.
    ///
    /// **Known problems:** If macros are imported through the wildcard, this macro is not included
    /// by the suggestion and has to be added by hand.
    ///
    /// Applying the suggestion when explicit imports of the things imported with a glob import
    /// exist, may result in `unused_imports` warnings.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// use crate1::*;
    ///
    /// foo();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// use crate1::foo;
    ///
    /// foo();
    /// ```
    pub WILDCARD_IMPORTS,
    pedantic,
    "lint `use _::*` statements"
}

declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);

impl LateLintPass<'_, '_> for WildcardImports {
    fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
        if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
            return;
        }
        if_chain! {
            if !in_macro(item.span);
            if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
            // don't lint prelude glob imports
            if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
            let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner_def_id());
            if !used_imports.is_empty(); // Already handled by `unused_imports`
            then {
                let mut applicability = Applicability::MachineApplicable;
                let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
                let (span, braced_glob) = if import_source_snippet.is_empty() {
                    // This is a `_::{_, *}` import
                    // In this case `use_path.span` is empty and ends directly in front of the `*`,
                    // so we need to extend it by one byte.
                    (
                        use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
                        true,
                    )
                } else {
                    // In this case, the `use_path.span` ends right before the `::*`, so we need to
                    // extend it up to the `*`. Since it is hard to find the `*` in weird
                    // formattings like `use _ ::  *;`, we extend it up to, but not including the
                    // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
                    // can just use the end of the item span
                    let mut span = use_path.span.with_hi(item.span.hi());
                    if snippet(cx, span, "").ends_with(';') {
                        span = use_path.span.with_hi(item.span.hi() - BytePos(1));
                    }
                    (
                        span,
                        false,
                    )
                };

                let imports_string = if used_imports.len() == 1 {
                    used_imports.iter().next().unwrap().to_string()
                } else {
                    let mut imports = used_imports
                        .iter()
                        .map(ToString::to_string)
                        .collect::<Vec<_>>();
                    imports.sort();
                    if braced_glob {
                        imports.join(", ")
                    } else {
                        format!("{{{}}}", imports.join(", "))
                    }
                };

                let sugg = if braced_glob {
                    imports_string
                } else {
                    format!("{}::{}", import_source_snippet, imports_string)
                };

                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
                    (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
                } else {
                    (WILDCARD_IMPORTS, "usage of wildcard import")
                };

                span_lint_and_sugg(
                    cx,
                    lint,
                    span,
                    message,
                    "try",
                    sugg,
                    applicability,
                );
            }
        }
    }
}
<commit_msg>Auto merge of #5312 - joshtriplett:wildcard-prelude-documentation, r=flip1995<commit_after>use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
    def::{DefKind, Res},
    Item, ItemKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::BytePos;

declare_clippy_lint! {
    /// **What it does:** Checks for `use Enum::*`.
    ///
    /// **Why is this bad?** It is usually better style to use the prefixed name of
    /// an enumeration variant, rather than importing variants.
    ///
    /// **Known problems:** Old-style enumerations that prefix the variants are
    /// still around.
    ///
    /// **Example:**
    /// ```rust
    /// use std::cmp::Ordering::*;
    /// ```
    pub ENUM_GLOB_USE,
    pedantic,
    "use items that import all variants of an enum"
}

declare_clippy_lint! {
    /// **What it does:** Checks for wildcard imports `use _::*`.
    ///
    /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
    /// you try to import something through a wildcard, that already has been imported by name from
    /// a different source:
    ///
    /// ```rust,ignore
    /// use crate1::foo; // Imports a function named foo
    /// use crate2::*; // Has a function named foo
    ///
    /// foo(); // Calls crate1::foo
    /// ```
    ///
    /// This can lead to confusing error messages at best and to unexpected behavior at worst.
    ///
    /// Note that this will not warn about wildcard imports from modules named `prelude`; many
    /// crates (including the standard library) provide modules named "prelude" specifically
    /// designed for wildcard import.
    ///
    /// **Known problems:** If macros are imported through the wildcard, this macro is not included
    /// by the suggestion and has to be added by hand.
    ///
    /// Applying the suggestion when explicit imports of the things imported with a glob import
    /// exist, may result in `unused_imports` warnings.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// use crate1::*;
    ///
    /// foo();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// use crate1::foo;
    ///
    /// foo();
    /// ```
    pub WILDCARD_IMPORTS,
    pedantic,
    "lint `use _::*` statements"
}

declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);

impl LateLintPass<'_, '_> for WildcardImports {
    fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
        if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
            return;
        }
        if_chain! {
            if !in_macro(item.span);
            if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
            // don't lint prelude glob imports
            if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
            let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner_def_id());
            if !used_imports.is_empty(); // Already handled by `unused_imports`
            then {
                let mut applicability = Applicability::MachineApplicable;
                let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
                let (span, braced_glob) = if import_source_snippet.is_empty() {
                    // This is a `_::{_, *}` import
                    // In this case `use_path.span` is empty and ends directly in front of the `*`,
                    // so we need to extend it by one byte.
                    (
                        use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
                        true,
                    )
                } else {
                    // In this case, the `use_path.span` ends right before the `::*`, so we need to
                    // extend it up to the `*`. Since it is hard to find the `*` in weird
                    // formattings like `use _ ::  *;`, we extend it up to, but not including the
                    // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
                    // can just use the end of the item span
                    let mut span = use_path.span.with_hi(item.span.hi());
                    if snippet(cx, span, "").ends_with(';') {
                        span = use_path.span.with_hi(item.span.hi() - BytePos(1));
                    }
                    (
                        span,
                        false,
                    )
                };

                let imports_string = if used_imports.len() == 1 {
                    used_imports.iter().next().unwrap().to_string()
                } else {
                    let mut imports = used_imports
                        .iter()
                        .map(ToString::to_string)
                        .collect::<Vec<_>>();
                    imports.sort();
                    if braced_glob {
                        imports.join(", ")
                    } else {
                        format!("{{{}}}", imports.join(", "))
                    }
                };

                let sugg = if braced_glob {
                    imports_string
                } else {
                    format!("{}::{}", import_source_snippet, imports_string)
                };

                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
                    (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
                } else {
                    (WILDCARD_IMPORTS, "usage of wildcard import")
                };

                span_lint_and_sugg(
                    cx,
                    lint,
                    span,
                    message,
                    "try",
                    sugg,
                    applicability,
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;

use super::compiler::{Opdata, FormatStringIterator};
use super::x64data::Flags;

pub fn format_opdata_list(name: &str, data: &[Opdata]) -> String {
    let mut forms = Vec::new();
    for data in data {
        forms.extend(format_opdata(name, data));
    }
    forms.join("\n")
}

pub fn format_opdata(name: &str, data: &Opdata) -> Vec<String> {
    let opsizes = if data.flags.contains(Flags::AUTO_SIZE) {&b"qwd"[..]}
             else if data.flags.contains(Flags::AUTO_NO32) {&b"qw"[..]}
             else if data.flags.contains(Flags::AUTO_REXW) {&b"qd"[..]}
             else if data.flags.contains(Flags::AUTO_VEXL) {&b"ho"[..]}
             else if name == "monitorx"                    {&b"qwd"[..]}
             else                                          {&b"!"[..]};

    let mut forms = Vec::new();
    for opsize in opsizes.iter().cloned() {
        let mut buf = String::new();
        buf.push_str(">>> ");
        buf.push_str(name);
        let mut first = true;
        for (ty, size) in FormatStringIterator::new(data.args) {
            if first {
                buf.push_str(" ");
                first = false;
            } else {
                buf.push_str(", ");
            }
            buf.push_str(&format_arg(ty, size, opsize))
        }
        if data.flags.contains(Flags::X86_ONLY) {
            buf.push_str(" (x86 only)");
        }
        forms.push(buf);
    }
    forms
}

static REGS: [&'static str; 16] = ["a",  "d",  "c",   "b",   "bp",  "sp",  "si",  "di",
                                   "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"];
static SEGREGS: [&'static str; 6] = ["es", "cs", "ss", "ds", "fs", "gs"];

fn format_arg(ty: u8, mut size: u8, opsize: u8) -> Cow<'static, str> {
    if size == b'*' {
        size = if opsize == b'q' && (ty == b'i' || ty == b'o') {
            b'd'
        } else {
            opsize
        };
    }

    fn format_size(size: u8) -> &'static str {
        match size {
            b'b' => "8",
            b'w' => "16",
            b'd' => "32",
            b'f' => "48",
            b'q' => "64",
            b'p' => "80",
            b'o' => "128",
            b'h' => "256",
            _ => ""
        }
    }

    match ty {
        b'i' => format!("imm{}",      format_size(size)).into(),
        b'o' => format!("rel{}off",   format_size(size)).into(),
        b'm' => format!("mem{}",      format_size(size)).into(),
        b'k' => format!("vm32addr{}", format_size(size)).into(),
        b'l' => format!("vm64addr{}", format_size(size)).into(),
        b'r' => format!("reg{}",      format_size(size)).into(),
        b'f' => "st".into(),
        b'x' => "mm".into(),
        b'y' => (if size == b'h' {"ymm"} else {"xmm"}).into(),
        b's' => "segreg".into(),
        b'c' => "creg".into(),
        b'd' => "dreg".into(),
        b'b' => "bndreg".into(),
        b'v' => format!("reg/mem{}", format_size(size)).into(),
        b'u' => format!("mm/mem{}", format_size(size)).into(),
        b'w' => format!("{}mm/mem{}", if size == b'h' {"y"} else {"x"}, format_size(size)).into(),
        b'A'...b'P' => {
            let i = ty as usize - 'A' as usize;
            match size {
                b'b' => if i < 4 { format!("{}l", REGS[i]).into() }
                   else if i < 8 { REGS[i].into() }
                   else          { format!("{}b", REGS[i]).into() },
                b'w' => if i < 4 { format!("{}x", REGS[i]).into() }
                   else if i < 8 { REGS[i].into() }
                   else          { format!("{}w", REGS[i]).into() },
                b'd' => if i < 4 { format!("e{}x",REGS[i]).into() }
                   else if i < 8 { format!("e{}", REGS[i]).into() }
                   else          { format!("{}d", REGS[i]).into() },
                b'q' => if i < 4 { format!("r{}x",REGS[i]).into() }
                   else if i < 8 { format!("r{}", REGS[i]).into() }
                   else          { format!("r{}", REGS[i]).into() },
                _ => panic!("invalid formatting data")
            }
        },
        b'Q'...b'V' => SEGREGS[ty as usize - 'Q' as usize].into(),
        b'W' => "cr8".into(),
        b'X' => "st0".into(),
        _ => panic!("invalid formatting data")
    }
}
<commit_msg>Emit feature information in the instruction overview<commit_after>use std::borrow::Cow;

use super::compiler::{Opdata, FormatStringIterator};
use super::x64data::Flags;

pub fn format_opdata_list(name: &str, data: &[Opdata]) -> String {
    let mut forms = Vec::new();
    for data in data {
        forms.extend(format_opdata(name, data));
    }
    forms.join("\n")
}

pub fn format_opdata(name: &str, data: &Opdata) -> Vec<String> {
    let opsizes = if data.flags.contains(Flags::AUTO_SIZE) {&b"qwd"[..]}
             else if data.flags.contains(Flags::AUTO_NO32) {&b"qw"[..]}
             else if data.flags.contains(Flags::AUTO_REXW) {&b"qd"[..]}
             else if data.flags.contains(Flags::AUTO_VEXL) {&b"ho"[..]}
             else if name == "monitorx"                    {&b"qwd"[..]}
             else                                          {&b"!"[..]};

    let mut forms = Vec::new();
    for opsize in opsizes.iter().cloned() {
        let mut buf = String::new();
        buf.push_str(">>> ");
        buf.push_str(name);
        let mut first = true;
        for (ty, size) in FormatStringIterator::new(data.args) {
            if first {
                buf.push_str(" ");
                first = false;
            } else {
                buf.push_str(", ");
            }
            buf.push_str(&format_arg(ty, size, opsize))
        }
        if data.flags.contains(Flags::X86_ONLY) {
            for _ in buf.len() .. 45 {
                buf.push(' ');
            }
            buf.push_str(" (x86 only)");
        }
        if !data.features.is_empty() {
            for _ in buf.len() .. 45 {
                buf.push(' ');
            }
            buf.push_str(&format!(" ({})", data.features));
        }
        forms.push(buf);
    }
    forms
}

static REGS: [&'static str; 16] = ["a",  "d",  "c",   "b",   "bp",  "sp",  "si",  "di",
                                   "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"];
static SEGREGS: [&'static str; 6] = ["es", "cs", "ss", "ds", "fs", "gs"];

fn format_arg(ty: u8, mut size: u8, opsize: u8) -> Cow<'static, str> {
    if size == b'*' {
        size = if opsize == b'q' && (ty == b'i' || ty == b'o') {
            b'd'
        } else {
            opsize
        };
    }

    fn format_size(size: u8) -> &'static str {
        match size {
            b'b' => "8",
            b'w' => "16",
            b'd' => "32",
            b'f' => "48",
            b'q' => "64",
            b'p' => "80",
            b'o' => "128",
            b'h' => "256",
            _ => ""
        }
    }

    match ty {
        b'i' => format!("imm{}",      format_size(size)).into(),
        b'o' => format!("rel{}off",   format_size(size)).into(),
        b'm' => format!("mem{}",      format_size(size)).into(),
        b'k' => format!("vm32addr{}", format_size(size)).into(),
        b'l' => format!("vm64addr{}", format_size(size)).into(),
        b'r' => format!("reg{}",      format_size(size)).into(),
        b'f' => "st".into(),
        b'x' => "mm".into(),
        b'y' => (if size == b'h' {"ymm"} else {"xmm"}).into(),
        b's' => "segreg".into(),
        b'c' => "creg".into(),
        b'd' => "dreg".into(),
        b'b' => "bndreg".into(),
        b'v' => format!("reg/mem{}", format_size(size)).into(),
        b'u' => format!("mm/mem{}", format_size(size)).into(),
        b'w' => format!("{}mm/mem{}", if size == b'h' {"y"} else {"x"}, format_size(size)).into(),
        b'A'...b'P' => {
            let i = ty as usize - 'A' as usize;
            match size {
                b'b' => if i < 4 { format!("{}l", REGS[i]).into() }
                   else if i < 8 { REGS[i].into() }
                   else          { format!("{}b", REGS[i]).into() },
                b'w' => if i < 4 { format!("{}x", REGS[i]).into() }
                   else if i < 8 { REGS[i].into() }
                   else          { format!("{}w", REGS[i]).into() },
                b'd' => if i < 4 { format!("e{}x",REGS[i]).into() }
                   else if i < 8 { format!("e{}", REGS[i]).into() }
                   else          { format!("{}d", REGS[i]).into() },
                b'q' => if i < 4 { format!("r{}x",REGS[i]).into() }
                   else if i < 8 { format!("r{}", REGS[i]).into() }
                   else          { format!("r{}", REGS[i]).into() },
                _ => panic!("invalid formatting data")
            }
        },
        b'Q'...b'V' => SEGREGS[ty as usize - 'Q' as usize].into(),
        b'W' => "cr8".into(),
        b'X' => "st0".into(),
        _ => panic!("invalid formatting data")
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;

#[derive(Debug)]
enum Aminoacid {
    A,
    G,
    C,
    T,
    Undef,
}

static SCORES: [[i32; 4]; 4] = [[10, -1, -3, -4],
                                [-1, 7, -5, -3],
                                [-3, -5, 9, 0],
                                [-4, -3, 0, 8]];

fn get_score(a: char, b: char) -> i32 {
    let a_pos = match a {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    let b_pos = match b {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    SCORES[b_pos as usize][a_pos as usize]
}

fn print_matrix(sec_1: &str, sec_2: &str, mtrx: Vec<Vec<i32>>) {
    for row in mtrx.into_iter() {
        // println!("{:?}", row);
        for elem in row.into_iter() {
            print!("{}\t", elem);
        }
        println!("");
    }
}

fn align_secuence(sec_1: &str, sec_2: &str) {
    let len_1 = sec_1.len() + 1;
    let len_2 = sec_2.len() + 1;

    /********* Initialize matrix **********/
    let mut matrix: Vec<Vec<i32>> = vec![vec![0; len_1]; len_2];

    let mut init_val = -5;
    for i in 1..len_1 {
        matrix[0][i] = init_val;
        init_val -= 5;
    }

    init_val = -5;
    for i in 1..len_2 {
        matrix[i][0] = init_val;
        init_val -= 5;
    }
    /**************************************/

    let mut diag_val;
    let mut left_val;
    let mut uppr_val;

    let mut i = 1;
    for c2 in sec_2.chars() {
        let mut j = 1;
        for c1 in sec_1.chars() {
            let score = get_score(c2, c1);
            diag_val = score + matrix[i - 1][j - 1];
            left_val = -5 + matrix[i][j - 1];
            uppr_val = -5 + matrix[i - 1][j];
            matrix[i][j] = max(diag_val, max(left_val, uppr_val));
            // println!("[{}][{}]: {}\td: {}\tl: {}\tu: {}\t->\tval: {}",
            //          i,
            //          j,
            //          score,
            //          diag_val,
            //          left_val,
            //          uppr_val,
            //          matrix[i][j]);
            j += 1;
        }
        i += 1;
    }

    print_matrix(sec_1, sec_2, matrix);
}

fn main() {
    align_secuence("ACCGTCTT", "CGTCTT");
    // align_secuence("ACC", "CGT");
    // println!("Hello, world!");
}
<commit_msg>Sale xD<commit_after>use std::cmp::max;

#[derive(Debug)]
enum Aminoacid {
    A,
    G,
    C,
    T,
    Undef,
}

#[derive(Debug)]
enum Direction {
    Diagonal,
    Up,
    Left,
    Undef,
}

use Direction::*;

static SCORES: [[i32; 4]; 4] = [[10, -1, -3, -4],
                                [-1, 7, -5, -3],
                                [-3, -5, 9, 0],
                                [-4, -3, 0, 8]];

fn get_score(a: char, b: char) -> i32 {
    let a_pos = match a {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    let b_pos = match b {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    SCORES[b_pos as usize][a_pos as usize]
}

fn print_matrix(mtrx: &Vec<Vec<i32>>) {
    for row in mtrx.into_iter() {
        // println!("{:?}", row);
        for elem in row.into_iter() {
            print!("{}\t", elem);
        }
        println!("");
    }
}

fn align_secuence(sec_1: &str, sec_2: &str) {
    let len_1 = sec_1.len() + 1;
    let len_2 = sec_2.len() + 1;

    /********* Initialize matrix **********/
    let mut matrix: Vec<Vec<i32>> = vec![vec![0; len_1]; len_2];

    let mut init_val = -5;
    for i in 1..len_1 {
        matrix[0][i] = init_val;
        init_val -= 5;
    }

    init_val = -5;
    for i in 1..len_2 {
        matrix[i][0] = init_val;
        init_val -= 5;
    }
    /**************************************/

    let mut diag_val;
    let mut left_val;
    let mut uppr_val;

    let mut i = 1;
    let mut j = 1;
    for c2 in sec_2.chars() {
        j = 1;
        for c1 in sec_1.chars() {
            let score = get_score(c2, c1);
            diag_val = score + matrix[i - 1][j - 1];
            left_val = -5 + matrix[i][j - 1];
            uppr_val = -5 + matrix[i - 1][j];
            matrix[i][j] = max(diag_val, max(left_val, uppr_val));
            j += 1;
        }
        i += 1;
    }

    print_matrix(&matrix);

    /************** Alignment **************/
    let mut align = String::new();

    i = sec_2.len();
    j = sec_1.len();

    // println!("{}", sec_2.as_bytes()[i] as char);
    // println!("{}", sec_1.as_bytes()[j] as char);

    let mut direct = Diagonal;
    let mut greater;
    loop {
        print!("i: {} j: {} -> ", i, j);
        if i == 0 || j == 0 {
            break;
        }

        diag_val = matrix[i - 1][j - 1];
        left_val = matrix[i][j - 1];
        uppr_val = matrix[i - 1][j];
        greater = max(diag_val, max(left_val, uppr_val));

        println!("{}", greater);

        match direct {
            Diagonal => {
                align.push(sec_2.as_bytes()[i - 1] as char);
            }
            Up => {
                align.push('_');
            }
            Left => {
                align.push('_');
            }
            Undef => {}
        }

        if greater == diag_val {
            direct = Diagonal;
            i -= 1;
            j -= 1;
        } else if greater == left_val {
            direct = Left;
            j -= 1;
        } else if greater == uppr_val {
            direct = Up;
            i -= 1;
        }
    }
    // loop {
    //     if i == 0 || j == 0 {
    //         break;
    //     }

    //     diag_val = matrix[i - 1][j - 1];
    //     left_val = matrix[i][j - 1];
    //     uppr_val = matrix[i - 1][j];
    //     greater = max(diag_val, max(left_val, uppr_val));

    //     println!("{}", greater);

    //     if greater == diag_val {
    //         println!("diag");
    //         align.push(sec_2.as_bytes()[i - 1] as char);
    //         if i > 0 {  i -= 1; }
    //         if j > 0 {  j -= 1; }
    //     } else if greater == left_val {
    //         println!("left");
    //         align.push('_');
    //         if i > 0 {  i -= 1; }
    //     } else if greater == uppr_val {
    //         println!("up");
    //         align.push('_');
    //         if j > 0 {  j -= 1; }
    //     }
    // }
    println!("\nAlignment: {}", align.chars().rev().collect::<String>());
    /**************************************/
}

fn main() {
    align_secuence("ACCGTCTT", "CGTCTT");
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug)]
struct Matrix {
    elems: BTreeMap<String, BTreeMap<String, f32>>,
    keys: BTreeSet<String>,
}

impl Matrix {
    fn new() -> Matrix {
        Matrix {
            elems: BTreeMap::new(),
            keys: BTreeSet::new(),
        }
    }

    fn insert(&mut self, c1: &str, c2: &str, val: f32) {
        let mut ins_map = self.elems
            .entry(c1.to_string())
            .or_insert_with(BTreeMap::new);
        ins_map.insert(c2.to_string(), val);
        self.keys.insert(c1.to_string());
        self.keys.insert(c2.to_string());
    }

    fn len(&self) -> usize {
        self.keys.len()
    }

    fn get_d(&self, c1: &str, c2: &str) -> &f32 {
        match self.elems.get(c1) {
            Some(map) => {
                match map.get(c2) {
                    Some(val) => val,
                    None => &self.elems[c2][c1],
                }
            }
            None => {
                match self.elems.get(c2) {
                    Some(map) => {
                        match map.get(c1) {
                            Some(val) => val,
                            None => panic!("c1 wasn't found"),
                        }
                    }
                    None => panic!("c2 wasn't found"),
                }
            }
        }
    }

    fn lowest_d(&self) -> (f32, String, String) {
        let mut iter = self.keys.iter();
        let mut key_1 = match iter.next() {
            Some(val) => val,
            None => panic!("No elements in matrix"),
        };

        let mut lowest = (9999.9, key_1.clone(), key_1.clone());

        for key_2 in iter {
            let &d = self.get_d(key_1, key_2);

            if lowest.0 > d {
                lowest = (d, key_1.clone(), key_2.clone());
            }

            key_1 = key_2;
        }

        lowest
    }

    fn join(&mut self, k1: &str, k2: &str) -> Matrix {
        if !(self.keys.contains(k1) || self.keys.contains(k2)) {
            panic!("Keys aren't in matrix");
        }
        /********************************************************************/
        let var1 = &self.elems[k1];
        let var2 = &self.elems[k2];

        println!("{:?} > {:?}", var1, var2);
        /********************************************************************/
        let d_12 = self.get_d(k1, k2) / 2.0;
        let d_21 = self.get_d(k2, k1) / 2.0;
        println!("D_{}{} / 2 = {}", k1, k2, d_12);
        println!("D_{}{} / 2 = {}", k2, k1, d_21);
        /********************************************************************/
        let mut k = String::from(k1);
        k.push_str(k2);
        let mut elems = self.elems.clone();
        elems.remove(k1);
        elems.remove(k2);

        let mut keys = self.keys.clone();
        keys.remove(k1);
        keys.remove(k2);
        let rem_keys = keys.clone();

        let mut ret_m = Matrix {
            elems: elems,
            keys: keys,
        };

        for key in rem_keys {
            let val = self.get_d(k1, &key) + self.get_d(k2, &key) - self.get_d(k1, k2);
            ret_m.insert(&k, &key, val / 2.0);
        }

        println!("{}", k);

        ret_m
    }

    fn print_matrix(&self) {
        println!("{:#?}", self.elems);
    }
}

fn upgma(input: &[(&str, &str, f32)]) {
    let mut matrix = Matrix::new();

    for &(c1, c2, val) in input {
        matrix.insert(c1, c2, val);
    }

    matrix.print_matrix();
    let (_, key_1, key_2) = matrix.lowest_d();
    println!("Joining: {} and {}", key_1, key_2);
    matrix = matrix.join(&key_1, &key_2);
}

fn main() {
    let input = vec![/*("A", "A", 0.0)0,*/
                     ("A", "B", 10.0),
                     ("A", "C", 12.0),
                     ("A", "D", 10.0),
                     ("A", "E", 7.0),
                     //  ("B", "B", 0.0),
                     ("B", "C", 4.0),
                     ("B", "D", 4.0),
                     ("B", "E", 13.0),
                     //  ("C", "C", 0.0),
                     ("C", "D", 6.0),
                     ("C", "E", 15.0),
                     //  ("D", "D", 0.0),
                     ("D", "E", 13.0) /* ("E", "E", 0.0)*/];

    upgma(&input);
}
<commit_msg>Finished practica_19<commit_after>use std::collections::BTreeMap;

#[derive(Debug)]
struct Matrix {
    elems: Vec<Vec<f32>>,
    keys: BTreeMap<String, usize>,
}

impl Matrix {
    fn new(keys: &[&str]) -> Matrix {
        let e_matrix = vec![vec![0.0; keys.len()]; keys.len()];
        let mut k_indexes = BTreeMap::new();

        for key in keys {
            k_indexes.insert(key.to_string(), 0);
        }

        for (i, (_, index)) in k_indexes.iter_mut().enumerate() {
            *index = i;
        }

        Matrix {
            elems: e_matrix,
            keys: k_indexes,
        }
    }

    fn insert(&mut self, c1: &str, c2: &str, val: f32) {
        let i1 = self.keys[c1];
        let i2 = self.keys[c2];

        if i1 < i2 {
            self.elems[i1][i2] = val;
        } else {
            self.elems[i2][i1] = val;
        }
    }

    fn len(&self) -> usize {
        self.keys.len()
    }

    fn get_d(&self, c1: &str, c2: &str) -> &f32 {
        let i1 = self.keys[c1];
        let i2 = self.keys[c2];

        if i1 < i2 {
            &self.elems[i1][i2]
        } else {
            &self.elems[i2][i1]
        }
    }

    fn lowest_d(&self) -> (f32, String, String) {
        let mut lowest = (99999.9, "".to_string(), "".to_string());

        for key_1 in self.keys.keys() {
            for key_2 in self.keys.keys() {
                if key_1 != key_2 {
                    let &d = self.get_d(key_1, key_2);
                    if lowest.0 > d {
                        lowest = (d, key_1.to_string(), key_2.to_string());
                    }
                }
            }
        }

        lowest
    }

    fn join(&mut self, k1: String, k2: String) -> Matrix {
        let mut st_keys = self.keys.clone();
        st_keys.remove(&k1);
        st_keys.remove(&k2);

        let clone_keys = st_keys.clone();

        let mut new_key = if k1 < k2 { k1.clone() } else { k2.clone() };
        new_key.push_str(if k1 < k2 { k2.as_str() } else { k1.as_str() });

        let new_ins_key = new_key.clone();

        st_keys.insert(new_key, 0);
        let ret_keys: Vec<&str> = st_keys.iter().map(|a| a.0.as_ref()).collect();
        println!("{:?}", ret_keys);
        let mut ret_m = Matrix::new(&ret_keys);

        let clone_keys: Vec<&str> = clone_keys.iter().map(|a| a.0.as_ref()).collect();
        for key_1 in &clone_keys {
            for key_2 in &clone_keys {
                ret_m.insert(key_1, key_2, *self.get_d(key_1, key_2));
                // println!("{} <> {}", key_1, key_2);
            }

            let val_1 = *self.get_d(k1.as_ref(), key_1);
            let val_2 = *self.get_d(k2.as_ref(), key_1);
            ret_m.insert(new_ins_key.as_ref(), key_1, (val_1 + val_2) / 2.0);
        }

        ret_m
    }

    fn print_matrix(&self) {
        for row in &self.elems {
            println!("{:?}", row);
        }
    }
}

fn upgma(keys: &[&str], input: &[(&str, &str, f32)]) {
    let mut matrix = Matrix::new(keys);

    for &(c1, c2, val) in input {
        matrix.insert(c1, c2, val);
    }

    matrix.print_matrix();

    while matrix.len() > 2 {
        let (_, key_1, key_2) = matrix.lowest_d();
        println!("\nJoining: {} and {}", key_1, key_2);
        matrix = matrix.join(key_1, key_2);
        matrix.print_matrix();
    }
}

fn main() {
    let keys = vec!["A", "B", "C", "D", "E"];
    let input = vec![/*("A", "A", 0.0)0,*/
                     ("A", "B", 10.0),
                     ("A", "C", 12.0),
                     ("A", "D", 10.0),
                     ("A", "E", 7.0),
                     //  ("B", "B", 0.0),
                     ("B", "C", 4.0),
                     ("B", "D", 4.0),
                     ("B", "E", 13.0),
                     //  ("C", "C", 0.0),
                     ("C", "D", 6.0),
                     ("C", "E", 15.0),
                     //  ("D", "D", 0.0),
                     ("D", "E", 13.0) /* ("E", "E", 0.0)*/];

    upgma(&keys, &input);
}
<|endoftext|>


<commit_before>use std::{fmt, path::Path};

use crate::build::chromeos::keymap::CHROMEOS_KEYS;
use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use pahkat_client::types::repo::Index;
use serde::{Deserialize, Serialize};
use serde_json::{json, to_string_pretty};

use crate::{
    build::BuildStep,
    bundle::{
        layout::{chrome::ChromeOsKbdLayer, Transform},
        KbdgenBundle,
    },
    util::split_keys,
};

const KEYBOARD_TEMPLATE: &str = include_str!("../../../resources/template-chromeos-keyboard.js");

#[derive(Serialize, Deserialize)]
pub struct ChromeOsBackground {
    template: String,
    descriptor: IndexMap<LanguageTag, ChromeOsDescriptor>,
}

impl fmt::Display for ChromeOsBackground {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}\n", self.template)?;
        write!(f, "\n")?;
        write!(
            f,
            "const descriptor = {}\n",
            to_string_pretty(&self.descriptor).map_err(|err| fmt::Error)?
        )?;
        write!(f, "\nKeyboard.install(descriptor)")?;

        Ok(())
    }
}

#[derive(Serialize, Deserialize)]
pub struct ChromeOsDescriptor {
    pub dead_keys: IndexMap<ChromeOsKbdLayer, Vec<String>>,
    pub transforms: IndexMap<String, IndexMap<String, String>>,
    pub layers: IndexMap<ChromeOsKbdLayer, IndexMap<String, String>>,
}

impl fmt::Display for ChromeOsDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            to_string_pretty(&self.dead_keys).map_err(|err| fmt::Error)?
        )?;
        write!(
            f,
            "{}",
            to_string_pretty(&self.transforms).map_err(|err| fmt::Error)?
        )?;

        Ok(())
    }
}

pub struct GenerateChromeOs;

#[async_trait(?Send)]
impl BuildStep for GenerateChromeOs {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let background_file_path = output_path.join("background.js");

        let template = KEYBOARD_TEMPLATE.clone();

        let mut descriptor = IndexMap::new();

        let mut json_transforms: IndexMap<String, IndexMap<String, String>>;
        // layout information is to be aggregated into a descriptor and then appended
        // to the end of the template
        for (language_tag, layout) in &bundle.layouts {
            if let Some(chromeos_target) = &layout.chrome_os {
                let mut json_dead_keys = IndexMap::new();
                if let Some(dead_keys)  = &chromeos_target.dead_keys {
                    for (dead_key_name, dead_key_list) in dead_keys {
                        json_dead_keys.insert(dead_key_name.clone(), dead_key_list.to_vec());
                    }
                }
                if let Some(layout_transforms) = &layout.transforms {
                    let layout_file_path = bundle
                        .path
                        .join("layouts")
                        .join(format!("{}.yaml", layout.language_tag));

                    let yaml_file = std::fs::File::open(&layout_file_path).unwrap();
                    let original_layout_yaml: serde_yaml::Value =
                        serde_yaml::from_reader(yaml_file).unwrap();

                    let owning_transform = *&original_layout_yaml.get("transforms").unwrap();

                    json_transforms = serde_yaml::from_value(owning_transform.clone()).unwrap();
                } else {
                    json_transforms = IndexMap::new();
                }

                let layers = &chromeos_target.primary.layers;

                let mut json_layers = IndexMap::new();
                for (layer_name, key_map) in layers {
                    let key_map: Vec<String> = split_keys(&key_map);

                    tracing::debug!(
                        "iso len: {}; keymap len: {}",
                        CHROMEOS_KEYS.len(),
                        key_map.len()
                    );
                    if CHROMEOS_KEYS.len() > key_map.len() {
                        panic!(
                            r#"Provided layer does not have enough keys, expected {} keys but got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                            CHROMEOS_KEYS.len(),
                            key_map.len(),
                            language_tag.to_string(),
                            "Windows",
                            "Primary",
                            layer_name,
                            key_map
                        );
                    }

                    let mut modifiers = IndexMap::new();
                    for (cursor, (_iso_key, modifier_name)) in CHROMEOS_KEYS.iter().enumerate() {
                        modifiers.insert(modifier_name.clone(), key_map[cursor].clone());
                    }

                    json_layers.insert(layer_name.clone(), modifiers);
                }

                descriptor.insert(
                    language_tag.clone(),
                    ChromeOsDescriptor {
                        dead_keys: json_dead_keys,
                        transforms: json_transforms,
                        layers: json_layers,
                    },
                );
            }
        }

        let background = ChromeOsBackground {
            template: template.to_string(),
            descriptor,
        };

        std::fs::write(
            output_path.join(background_file_path),
            background.to_string(),
        )
        .unwrap();
    }
}
<commit_msg>generate chromeos manifest<commit_after>use std::{fmt, path::Path};

use crate::build::chromeos::keymap::CHROMEOS_KEYS;
use async_trait::async_trait;
use indexmap::IndexMap;
use language_tags::LanguageTag;
use pahkat_client::types::repo::Index;
use serde::{Deserialize, Serialize};
use serde_json::{json, to_string_pretty};

use crate::{
    build::BuildStep,
    bundle::{
        layout::{chrome::ChromeOsKbdLayer, Transform},
        KbdgenBundle,
    },
    util::split_keys,
};

const KEYBOARD_TEMPLATE: &str = include_str!("../../../resources/template-chromeos-keyboard.js");

#[derive(Serialize, Deserialize)]
pub struct ChromeOsBackground {
    template: String,
    descriptor: IndexMap<LanguageTag, ChromeOsDescriptor>,
}

#[derive(Serialize, Deserialize)]
pub struct ManifestBackground {
    scripts: Vec<String>,
}

#[derive(Serialize, Deserialize)]
pub struct ManifestInputComponent {
    name: String,
    #[serde(rename = "type")]
    input_type: String,
    id: String,
    description: String,
    language: String,
    layouts: Vec<String>,
}

impl ManifestInputComponent {
    fn from_config(language_tag: String, locale: LanguageTag, xkb_layout: String) -> Self {
        let underscore_name = format!("__MSG_{}__", language_tag.replace("-", "_"));
        Self {
            name: underscore_name.clone(),
            input_type: "ime".to_string(),
            id: language_tag.to_string(),
            description: underscore_name.clone(),
            language: locale.to_string(),
            layouts: vec![xkb_layout.to_string()], // should somehow be able to get more?
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct ManifestIcons {
    #[serde(rename = "16")]
    icon_16: String,
    #[serde(rename = "48")]
    icon_48: String,
    #[serde(rename = "128")]
    icon_128: String,
}

#[derive(Serialize, Deserialize)]
pub struct ChromeOsManifest {
    name: String,
    version: String,
    version_name: String,
    manifest_version: u8,
    description: String,
    background: ManifestBackground,
    permissions: Vec<String>,
    input_components: Vec<ManifestInputComponent>,
    default_locale: String,
    icons: ManifestIcons,
}

impl fmt::Display for ChromeOsBackground {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}\n", self.template)?;
        write!(f, "\n")?;
        write!(
            f,
            "const descriptor = {}\n",
            to_string_pretty(&self.descriptor).map_err(|err| fmt::Error)?
        )?;
        write!(f, "\nKeyboard.install(descriptor)")?;

        Ok(())
    }
}

#[derive(Serialize, Deserialize)]
pub struct ChromeOsDescriptor {
    pub dead_keys: IndexMap<ChromeOsKbdLayer, Vec<String>>,
    pub transforms: IndexMap<String, IndexMap<String, String>>,
    pub layers: IndexMap<ChromeOsKbdLayer, IndexMap<String, String>>,
}

impl fmt::Display for ChromeOsDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            to_string_pretty(&self.dead_keys).map_err(|err| fmt::Error)?
        )?;
        write!(
            f,
            "{}",
            to_string_pretty(&self.transforms).map_err(|err| fmt::Error)?
        )?;

        Ok(())
    }
}

pub struct GenerateChromeOs;

#[async_trait(?Send)]
impl BuildStep for GenerateChromeOs {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let background_file_path = output_path.join("background.js");
        let manifest_file_path = output_path.join("manifest.json");

        let template = KEYBOARD_TEMPLATE.clone();

        let mut descriptor = IndexMap::new();

        let mut json_transforms: IndexMap<String, IndexMap<String, String>>;

        let mut manifest_input_components: Vec<ManifestInputComponent> = Vec::new();
        // layout information is to be aggregated into a descriptor and then appended
        // to the end of the template
        for (language_tag, layout) in &bundle.layouts {
            if let Some(chromeos_target) = &layout.chrome_os {
                let input_component = ManifestInputComponent::from_config(
                    language_tag.to_string(),
                    chromeos_target
                        .config
                        .locale
                        .as_ref()
                        .map(|x| x.clone())
                        .unwrap_or_else(|| "en-US".parse().unwrap()),
                    chromeos_target
                        .config
                        .xkb_layout
                        .clone()
                        .unwrap_or_else(|| "us".to_string()),
                );

                manifest_input_components.push(input_component);

                let mut json_dead_keys = IndexMap::new();
                if let Some(dead_keys) = &chromeos_target.dead_keys {
                    for (dead_key_name, dead_key_list) in dead_keys {
                        json_dead_keys.insert(dead_key_name.clone(), dead_key_list.to_vec());
                    }
                }
                if let Some(layout_transforms) = &layout.transforms {
                    let layout_file_path = bundle
                        .path
                        .join("layouts")
                        .join(format!("{}.yaml", layout.language_tag));

                    let yaml_file = std::fs::File::open(&layout_file_path).unwrap();
                    let original_layout_yaml: serde_yaml::Value =
                        serde_yaml::from_reader(yaml_file).unwrap();

                    let owning_transform = *&original_layout_yaml.get("transforms").unwrap();

                    json_transforms = serde_yaml::from_value(owning_transform.clone()).unwrap();
                } else {
                    json_transforms = IndexMap::new();
                }

                let layers = &chromeos_target.primary.layers;

                let mut json_layers = IndexMap::new();
                for (layer_name, key_map) in layers {
                    let key_map: Vec<String> = split_keys(&key_map);

                    tracing::debug!(
                        "iso len: {}; keymap len: {}",
                        CHROMEOS_KEYS.len(),
                        key_map.len()
                    );
                    if CHROMEOS_KEYS.len() > key_map.len() {
                        panic!(
                            r#"Provided layer does not have enough keys, expected {} keys but got {}, in {}:{}:{}:{:?}: \n{:?}"#,
                            CHROMEOS_KEYS.len(),
                            key_map.len(),
                            language_tag.to_string(),
                            "Windows",
                            "Primary",
                            layer_name,
                            key_map
                        );
                    }

                    let mut modifiers = IndexMap::new();
                    for (cursor, (_iso_key, modifier_name)) in CHROMEOS_KEYS.iter().enumerate() {
                        modifiers.insert(modifier_name.clone(), key_map[cursor].clone());
                    }

                    json_layers.insert(layer_name.clone(), modifiers);
                }

                descriptor.insert(
                    language_tag.clone(),
                    ChromeOsDescriptor {
                        dead_keys: json_dead_keys,
                        transforms: json_transforms,
                        layers: json_layers,
                    },
                );
            }
        }

        let background = ChromeOsBackground {
            template: template.to_string(),
            descriptor,
        };

        std::fs::write(
            output_path.join(background_file_path),
            background.to_string(),
        )
        .unwrap();

        if let Some(target) = &bundle.targets.chromeos {
            let manifest = ChromeOsManifest {
                name: "__MSG_name__".to_string(),
                version: target.build.clone(),
                version_name: target.version.clone(),
                manifest_version: 2,
                description: "__MSG_description__".to_string(),
                background: ManifestBackground {
                    scripts: vec!["background.js".to_string()],
                },
                permissions: vec!["input".to_string()],
                input_components: manifest_input_components,
                default_locale: "en".to_string(),
                icons: ManifestIcons {
                    icon_16: "icon16.png".to_string(),
                    icon_48: "icon48.png".to_string(),
                    icon_128: "icon128.png".to_string(),
                },
            };

            std::fs::write(
                output_path.join(manifest_file_path),
                serde_json::to_string_pretty(&manifest).unwrap(),
            )
            .unwrap();
        }
    }
}
<|endoftext|>