<commit_before>//! Symbol frequency model implemented by Binary Indexed Tree.

use std::boxed::Box;
use std::vec::Vec;
use super::Model;
use super::Parameters;
use super::super::Result;
use super::super::Error;

/// Adaptive model that uses a Binary Indexed Tree for storing cumulative frequencies.
pub struct AdaptiveTreeModel {
    /// Tree of cumulative frequencies.
    tree: Vec<u64>,
    /// Arithmetic parameters.
    params: Parameters,
}

trait LastOne<T> {
    fn last_one(self) -> T;
}

impl LastOne<usize> for usize {
    fn last_one(self) -> usize {
        self & self.wrapping_neg()
    }
}

impl AdaptiveTreeModel {
    /// Initializes the model with the given parameters.
    pub fn new(p: Parameters) -> Box<AdaptiveTreeModel> {
        let mut m = AdaptiveTreeModel {
            tree: vec![0; p.symbol_count + 1],
            params: p,
        };
        for i in 1..m.params.symbol_count {
            m.tree[i] = i.last_one() as u64;
        }

        return Box::new(m);
    }

    /// Updates the cumulative frequencies for the given symbol.
    fn update(&mut self, symbol: usize) {
    }
}

impl Model for AdaptiveTreeModel {
    fn parameters<'a>(&'a self) -> &'a Parameters {
        &self.params
    }

    fn total_frequency(&self) -> u64 {
        0
    }

    fn get_frequency(&mut self, symbol: usize) -> Result<(u64, u64)> {
        if symbol > self.params.symbol_eof {
            return Err(Error::InvalidInput)
        }

        let mut i = symbol;
        let mut sum = 0u64;
        while i > 0 {
            sum += self.tree[i];
            i -= i.last_one();
        }
        return Ok((sum, 0));
    }

    fn get_symbol(&mut self, value: u64) -> Result<(usize, u64, u64)> {
        Err(Error::InvalidInput)
    }
}

<commit_msg>Added tree manipulation code for encoding<commit_after>//! Symbol frequency model implemented by Binary Indexed Tree.

use std::boxed::Box;
use std::vec::Vec;
use super::Model;
use super::Parameters;
use super::super::Result;
use super::super::Error;

/// Adaptive model that uses a Binary Indexed Tree for storing cumulative frequencies.
pub struct AdaptiveTreeModel {
    /// Tree of cumulative frequencies.
    tree: Vec<u64>,
    /// Arithmetic parameters.
    params: Parameters,
}

trait LastOne<T> {
    fn last_one(self) -> T;
}

impl LastOne<usize> for usize {
    fn last_one(self) -> usize {
        self & self.wrapping_neg()
    }
}

impl AdaptiveTreeModel {
    /// Initializes the model with the given parameters.
    pub fn new(p: Parameters) -> Box<AdaptiveTreeModel> {
        let mut m = AdaptiveTreeModel {
            tree: vec![0; p.symbol_count],
            params: p,
        };

        m.tree[0] = 1u64;
        for i in 1..m.tree.len() {
            m.tree[i] = i.last_one() as u64;
        }

        return Box::new(m);
    }

    /// Returns the cumulated frequency of the symbol
    fn get_frequency_single(&self, symbol: usize) -> u64 {
        let mut i = symbol;
        let mut sum = self.tree[0];
        while i > 0 {
            sum += self.tree[i];
            i -= i.last_one();
        }
        return sum;
    }

    /// Updates the cumulative frequencies for the given symbol.
    fn update(&mut self, symbol: usize) {
        if symbol == 0 {
            self.tree[0] += 1;
        } else {
            let mut i = symbol;
            while i <= self.params.symbol_eof {
                self.tree[i] += 1;
                i += i.last_one();
            }
        }
    }
}

impl Model for AdaptiveTreeModel {
    fn parameters<'a>(&'a self) -> &'a Parameters {
        &self.params
    }

    fn total_frequency(&self) -> u64 {
        self.get_frequency_single(self.params.symbol_eof)
    }

    fn get_frequency(&mut self, symbol: usize) -> Result<(u64, u64)> {
        if symbol > self.params.symbol_eof {
            return Err(Error::InvalidInput);
        }

        let result = if symbol > 0 {
            let mut sumh = 0u64;
            let mut suml = 0u64;
            let mut h = symbol;
            let mut l = symbol - 1;
            while h != l {
                if h > l {
                    sumh += self.tree[h];
                    h -= h.last_one();
                } else {
                    suml += self.tree[l];
                    l -= l.last_one();
                }
            }

            let sumr = self.get_frequency_single(h);
            (suml + sumr, sumh + sumr)
        } else {
            (0u64, self.tree[0])
        };

        self.update(symbol);
        return Ok(result);
    }

    fn get_symbol(&mut self, value: u64) -> Result<(usize, u64, u64)> {
        Err(Error::InvalidInput)
    }
}

<|endoftext|>


<commit_before>use buffer::operation::Operation;
use buffer::{Buffer, Position, range};
use std::clone::Clone;

/// A reversible buffer insert operation.
///
/// Inserts the provided content at the specified position. Tracks both, and reverses
/// the operation by calculating the content's start and end positions (range), relative
/// to its inserted location, and removing said range from the underlying buffer.
#[derive(Clone)]
pub struct Insert {
    content: String,
    position: Position,
}

impl Operation for Insert {
    fn run(&mut self, buffer: &mut Buffer) {
        buffer.data.borrow_mut().insert(&self.content, &self.position);

        // We've modified the buffer, but it doesn't know that. Bust its cache.
        buffer.clear_caches()
    }

    // We need to calculate the range of the inserted content.
    // The start of the range corresponds to the cursor position at the time of the insert,
    // which we've stored. Finding the end of the range requires that we dig into the content.
    fn reverse(&mut self, buffer: &mut Buffer) {
        // The line count of the content tells us the line number for the end of the
        // range (just add the number of new lines to the starting line).
        let line_count = self.content.chars().filter(|&c| c == '\n').count() + 1;
        let end_line = self.position.line + line_count - 1;

        let end_offset = if line_count == 1 {
            // If there's only one line, the range starts and ends on the same line, and so its
            // offset needs to take the original insertion location into consideration.
            self.position.offset + self.content.len()
        } else {
            // If there are multiple lines, the end of the range doesn't
            // need to consider the original insertion location.
            match self.content.lines().last() {
                Some(line) => line.len(),
                None => return,
            }
        };

        // Now that we have the required info,
        // build the end position and total range.
        let end_position = Position{
            line: end_line,
            offset: end_offset,
        };
        let range = range::new(
            self.position.clone(),
            end_position
        );

        // Remove the content we'd previously inserted.
        buffer.data.borrow_mut().delete(&range);

        // We've modified the buffer, but it doesn't know that. Bust its cache.
        buffer.clear_caches()
    }

    fn clone_operation(&self) -> Box<Operation> {
        Box::new(self.clone())
    }
}

pub fn new(content: String, position: Position) -> Insert {
    Insert{ content: content, position: position }
}

impl Buffer {
    /// Inserts `data` into the buffer at the cursor position.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn insert(&mut self, data: &str) {
        // Build and run an insert operation.
        let mut op = new(data.to_string(), self.cursor.position.clone());
        op.run(self);

        // Store the operation in the history
        // object so that it can be undone.
        match self.operation_group {
            Some(ref mut group) => group.add(Box::new(op)),
            None => self.history.add(Box::new(op)),
        };

        // Caches are invalid as the buffer has changed.
        self.clear_caches();
    }
}

#[cfg(test)]
mod tests {
    use super::new;
    use buffer::position::Position;
    use buffer::operation::Operation;

    #[test]
    fn run_and_reverse_add_and_remove_content_without_newlines_at_cursor_position() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        buffer.insert(&"something");

        // Set up a position pointing to the end of the buffer's contents.
        let insert_position = Position{ line: 0, offset: 9 };

        // Create the insert operation and run it.
        let mut insert_operation = super::new(" else".to_string(), insert_position);
        insert_operation.run(&mut buffer);

        assert_eq!(buffer.data(), "something else");

        insert_operation.reverse(&mut buffer);

        assert_eq!(buffer.data(), "something");
    }

    #[test]
    fn run_and_reverse_add_and_remove_content_with_newlines_at_cursor_position() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        buffer.insert(&"\n something");

        // Set up a position pointing to the end of the buffer's contents.
        let insert_position = Position{ line: 1, offset: 10 };

        // Create the insert operation and run it.
        //
        // NOTE: The newline character ensures that the operation doesn't use a naive
        //       algorithm based purely on the content length.
        let mut insert_operation = super::new("\n else\n entirely".to_string(), insert_position);
        insert_operation.run(&mut buffer);

        assert_eq!(buffer.data(), "\n something\n else\n entirely");

        insert_operation.reverse(&mut buffer);

        assert_eq!(buffer.data(), "\n something");
    }

    #[test]
    fn reverse_removes_a_newline() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        let mut insert_operation = super::new("\n".to_string(), Position{ line: 0, offset: 0 });
        insert_operation.run(&mut buffer);
        assert_eq!(buffer.data(), "\n");

        insert_operation.reverse(&mut buffer);
        assert_eq!(buffer.data(), "");
    }
}
<commit_msg>Use split instead of lines; we care about trailing newlines.<commit_after>use buffer::operation::Operation;
use buffer::{Buffer, Position, range};
use std::clone::Clone;

/// A reversible buffer insert operation.
///
/// Inserts the provided content at the specified position. Tracks both, and reverses
/// the operation by calculating the content's start and end positions (range), relative
/// to its inserted location, and removing said range from the underlying buffer.
#[derive(Clone)]
pub struct Insert {
    content: String,
    position: Position,
}

impl Operation for Insert {
    fn run(&mut self, buffer: &mut Buffer) {
        buffer.data.borrow_mut().insert(&self.content, &self.position);

        // We've modified the buffer, but it doesn't know that. Bust its cache.
        buffer.clear_caches()
    }

    // We need to calculate the range of the inserted content.
    // The start of the range corresponds to the cursor position at the time of the insert,
    // which we've stored. Finding the end of the range requires that we dig into the content.
    fn reverse(&mut self, buffer: &mut Buffer) {
        // The line count of the content tells us the line number for the end of the
        // range (just add the number of new lines to the starting line).
        let line_count = self.content.chars().filter(|&c| c == '\n').count() + 1;
        let end_line = self.position.line + line_count - 1;

        let end_offset = if line_count == 1 {
            // If there's only one line, the range starts and ends on the same line, and so its
            // offset needs to take the original insertion location into consideration.
            self.position.offset + self.content.len()
        } else {
            // If there are multiple lines, the end of the range doesn't
            // need to consider the original insertion location.
            match self.content.split("\n").last() {
                Some(line) => line.len(),
                None => return,
            }
        };

        // Now that we have the required info,
        // build the end position and total range.
        let end_position = Position{
            line: end_line,
            offset: end_offset,
        };
        let range = range::new(
            self.position.clone(),
            end_position
        );

        // Remove the content we'd previously inserted.
        buffer.data.borrow_mut().delete(&range);

        // We've modified the buffer, but it doesn't know that. Bust its cache.
        buffer.clear_caches()
    }

    fn clone_operation(&self) -> Box<Operation> {
        Box::new(self.clone())
    }
}

pub fn new(content: String, position: Position) -> Insert {
    Insert{ content: content, position: position }
}

impl Buffer {
    /// Inserts `data` into the buffer at the cursor position.
    ///
    /// # Examples
    ///
    /// ```
    /// let mut buffer = scribe::buffer::new();
    /// buffer.insert("scribe");
    /// assert_eq!(buffer.data(), "scribe");
    /// ```
    pub fn insert(&mut self, data: &str) {
        // Build and run an insert operation.
        let mut op = new(data.to_string(), self.cursor.position.clone());
        op.run(self);

        // Store the operation in the history
        // object so that it can be undone.
        match self.operation_group {
            Some(ref mut group) => group.add(Box::new(op)),
            None => self.history.add(Box::new(op)),
        };

        // Caches are invalid as the buffer has changed.
        self.clear_caches();
    }
}

#[cfg(test)]
mod tests {
    use super::new;
    use buffer::position::Position;
    use buffer::operation::Operation;

    #[test]
    fn run_and_reverse_add_and_remove_content_without_newlines_at_cursor_position() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        buffer.insert(&"something");

        // Set up a position pointing to the end of the buffer's contents.
        let insert_position = Position{ line: 0, offset: 9 };

        // Create the insert operation and run it.
        let mut insert_operation = super::new(" else".to_string(), insert_position);
        insert_operation.run(&mut buffer);

        assert_eq!(buffer.data(), "something else");

        insert_operation.reverse(&mut buffer);

        assert_eq!(buffer.data(), "something");
    }

    #[test]
    fn run_and_reverse_add_and_remove_content_with_newlines_at_cursor_position() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        buffer.insert(&"\n something");

        // Set up a position pointing to the end of the buffer's contents.
        let insert_position = Position{ line: 1, offset: 10 };

        // Create the insert operation and run it.
        //
        // NOTE: The newline character ensures that the operation doesn't use a naive
        //       algorithm based purely on the content length.
        let mut insert_operation = super::new("\n else\n entirely".to_string(), insert_position);
        insert_operation.run(&mut buffer);

        assert_eq!(buffer.data(), "\n something\n else\n entirely");

        insert_operation.reverse(&mut buffer);

        assert_eq!(buffer.data(), "\n something");
    }

    #[test]
    fn reverse_removes_a_newline() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        let mut insert_operation = super::new("\n".to_string(), Position{ line: 0, offset: 0 });
        insert_operation.run(&mut buffer);
        assert_eq!(buffer.data(), "\n");

        insert_operation.reverse(&mut buffer);
        assert_eq!(buffer.data(), "");
    }

    #[test]
    fn reverse_correctly_removes_line_ranges() {
        // Set up a buffer with some data.
        let mut buffer = ::buffer::new();
        buffer.insert(&"scribe\nlibrary\n");

        let mut insert_operation = super::new("editor\n".to_string(), Position{ line: 1, offset: 0 });
        insert_operation.run(&mut buffer);
        assert_eq!(buffer.data(), "scribe\neditor\nlibrary\n");

        insert_operation.reverse(&mut buffer);
        assert_eq!(buffer.data(), "scribe\nlibrary\n");
    }
}
<|endoftext|>


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

use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use crate::{build::BuildStep, bundle::KbdgenBundle};

const REPOSITORY: &str = "repo";
const HOSTING_APP: &str = "HostingApp";
const HOSTING_INFO_STRINGS: &str = "InfoPlist.strings";

const KEYBOARD: &str = "Keyboard";
const INFO_PLIST: &str = "Info.plist";

const SETTINGS_BUNDLE: &str = "Settings.bundle";
const ROOT_PLIST: &str = "Root.plist";
const ENTITLEMENTS_EXTENSION: &str = ".entitlements";

#[derive(Serialize, Deserialize, Debug)]
pub struct XcodeHostingInfoStrings {
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
}

impl fmt::Display for XcodeHostingInfoStrings {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&format!(
            "\"CFBundleName\" = {:?};\n\"CFBundleDisplayName\" = {:?};",
            self.cf_bundle_name, self.cf_bundle_display_name
        ))
    }
}

// LAYOUT PLIST START

#[derive(Serialize, Deserialize, Debug)]
pub struct LayoutInfoPlistExtensionAttributes {
    #[serde(rename = "IsASCIICapable")]
    is_ascii_capable: bool,
    #[serde(rename = "PrefersRightToLeft")]
    prefers_right_to_left: bool,
    #[serde(rename = "PrimaryLanguage")]
    primary_language: String,
    #[serde(rename = "RequestsOpenAccess")]
    requests_open_access: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct KeyboardInfoPlistExtension {
    #[serde(rename = "NSExtensionAttributes")]
    ns_extension_attributes: LayoutInfoPlistExtensionAttributes,
    #[serde(rename = "NSExtensionPointIdentifier")]
    ns_extension_point_identifier: String,
    #[serde(rename = "NSExtensionPrincipalClass")]
    ns_extension_principal_class: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct KeyboardInfoPlist {
    #[serde(rename = "DivvunSpellerPath")]
    divvun_speller_path: String,
    #[serde(rename = "DivvunSpellerPackageKey")]
    divvun_speller_package_key: String,
    #[serde(rename = "CFBundleDevelopmentRegion")]
    cf_bundle_development_region: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
    #[serde(rename = "CFBundleExecutable")]
    cf_bundle_executable: String,
    #[serde(rename = "CFBundleIdentifier")]
    cf_bundle_identifier: String,
    #[serde(rename = "CFBundleInfoDictionaryVersion")]
    cf_bundle_info_dictionary_version: String,
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundlePackageType")]
    cf_bundle_package_type: String,
    #[serde(rename = "CFBundleShortVersionString")]
    cf_bundle_short_version_string: String,
    #[serde(rename = "CFBundleSignature")]
    cf_bundle_signature: String,
    #[serde(rename = "CFBundleVersion")]
    cf_bundle_version: String,
    #[serde(rename = "DivvunKeyboardIndex")]
    divvun_keyboard_index: usize,
    #[serde(rename = "ITSAppUsesNonExemptEncryption")]
    its_app_uses_non_exempt_encryption: bool,
    #[serde(rename = "LSApplicationQueriesSchemes")]
    ls_application_queries_schemes: Vec<String>,
    #[serde(rename = "NSExtension")]
    ns_extension: KeyboardInfoPlistExtension,
}

// LAYOUT PLIST END

#[derive(Serialize, Deserialize, Debug)]
pub struct EntitlementsDict {
    #[serde(rename = "com.apple.security.application-groups")]
    com_apple_security_application_groups: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PreferenceSpecifier {
    #[serde(rename = "Type")]
    preference_type: String,
    #[serde(rename = "Title")]
    title: String,
    #[serde(rename = "Key")]
    key: String,
    #[serde(rename = "DefaultValue")]
    default_value: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct SettingsRootDict {
    #[serde(rename = "StringsTable")]
    strings_table: String,
    #[serde(rename = "PreferenceSpecifiers")]
    preference_specifiers: Vec<PreferenceSpecifier>,
    #[serde(rename = "ApplicationGroupContainerIdentifier")]
    application_group_container_identifier: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct BundleSchemes {
    #[serde(rename = "CFBundleURLSchemes")]
    cf_bundle_url_schemes: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct HostingPlist {
    #[serde(rename = "CFBundleDevelopmentRegion")]
    cf_bundle_development_region: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
    #[serde(rename = "CFBundleExecutable")]
    cf_bundle_executable: String,
    #[serde(rename = "CFBundleIdentifier")]
    cf_bundle_identifier: String,
    #[serde(rename = "CFBundleInfoDictionaryVersion")]
    cf_bundle_info_dictionary_version: String,
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundlePackageType")]
    cf_bundle_package_type: String,
    #[serde(rename = "CFBundleShortVersionString")]
    cf_bundle_short_version_string: String,
    #[serde(rename = "CFBundleSignature")]
    cf_bundle_signature: String,
    #[serde(rename = "CFBundleURLTypes")]
    cf_bundle_url_types: Vec<BundleSchemes>,
    #[serde(rename = "CFBundleVersion")]
    cf_bundle_version: String,
    #[serde(rename = "ITSAppUsesNonExemptEncryption")]
    its_app_uses_non_exempt_encryption: bool,
    #[serde(rename = "LSApplicationQueriesSchemes")]
    ls_application_queries_schemes: Vec<String>,
    #[serde(rename = "LSRequiresIPhoneOS")]
    ls_requires_iphone_os: bool,
    #[serde(rename = "UIBackgroundModes")]
    ui_background_modes: Vec<String>,
    #[serde(rename = "UILaunchStoryboardName")]
    ui_launch_storyboard_name: String,
    #[serde(rename = "UIRequiredDeviceCapabilities")]
    ui_required_device_capabilities: Vec<String>,
    #[serde(rename = "UISupportedInterfaceOrientations")]
    ui_supported_interface_orientations: Vec<String>,
    #[serde(rename = "UIUserInterfaceStyle")]
    ui_user_interface_style: String,
}

pub fn replace_all_occurances(input: String, character: char, replace_with: char) -> String {
    input
        .as_str()
        .chars()
        .map(|curr| {
            if curr.cmp(&character) != Ordering::Equal {
                curr
            } else {
                replace_with
            }
        })
        .into_iter()
        .collect::<String>()
}

pub fn generate_keyboard_plist(template_path: PathBuf, value: IosSettings, output_path: PathBuf) {
    let mut keyboard_plist: KeyboardInfoPlist =
        plist::from_file(template_path.clone()).expect("valid stuff");

    keyboard_plist.cf_bundle_display_name = value.display_name;
    keyboard_plist.cf_bundle_short_version_string = value.short_version;
    keyboard_plist.cf_bundle_version = value.build_version;
    keyboard_plist.ls_application_queries_schemes[0] = value.package_id;
    keyboard_plist
        .ns_extension
        .ns_extension_attributes
        .primary_language = value.primary_language;
    keyboard_plist.divvun_keyboard_index = value.keyboard_index;

    plist::to_file_xml(output_path, &keyboard_plist).unwrap();
}

pub fn generate_hosting_plist(in_out_path: PathBuf, value: IosSettings) {
    let mut hosting_app_plist: HostingPlist =
        plist::from_file(in_out_path.clone()).expect("valid stuff");

    hosting_app_plist.cf_bundle_display_name = value.display_name;
    hosting_app_plist.cf_bundle_short_version_string = value.short_version;
    hosting_app_plist.cf_bundle_version = value.build_version;
    hosting_app_plist.cf_bundle_url_types[0].cf_bundle_url_schemes[0] = value.package_id.clone();
    hosting_app_plist.ls_application_queries_schemes[0] = value.package_id;

    plist::to_file_xml(in_out_path, &hosting_app_plist).unwrap();
}

pub fn update_entitlements(entitlements_path: PathBuf, new_entitlements: Vec<String>) {
    let mut keyboard_entitlements: EntitlementsDict =
        plist::from_file(entitlements_path.clone()).expect("valid stuff");
    keyboard_entitlements.com_apple_security_application_groups = new_entitlements;
    plist::to_file_xml(entitlements_path, &keyboard_entitlements).unwrap();
}

#[derive(Clone)]
pub struct IosSettings {
    display_name: String,
    short_version: String,
    build_version: String,
    package_id: String,
    primary_language: String,
    keyboard_index: usize,
}

pub struct GenerateXcode;

#[async_trait(?Send)]
impl BuildStep for GenerateXcode {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let repository_path = output_path.join(REPOSITORY);
        let hosting_app_path = repository_path.join(HOSTING_APP);
        let keyboard_path = repository_path.join(KEYBOARD);

        for (layout_index, (language_tag, layout)) in bundle.layouts.iter().enumerate() {
            if let Some(target) = &bundle.targets.ios {
                if let Some(_ios_layout) = &layout.i_os {
                    // GENERATE LOCALES
                    for (locale_name, locale_info) in &bundle.project.locales {
                        let locale_name = if locale_name == "en" {
                            "Base"
                        } else {
                            locale_name
                        };
                        let locale_path = hosting_app_path.join(&format!("{}.lproj", locale_name));
                        let info_path = locale_path.join(HOSTING_INFO_STRINGS);
                        println!("{:?}", locale_path);

                        std::fs::create_dir_all(&locale_path).unwrap();

                        let info_strings = XcodeHostingInfoStrings {
                            cf_bundle_name: locale_info.name.to_string(),
                            cf_bundle_display_name: locale_info.name.to_string(),
                        };
                        std::fs::write(info_path, info_strings.to_string()).unwrap();
                    }

                    // KEYBOARD PLIST
                    let keyboard_folder_name = replace_all_occurances(
                        bundle
                            .project
                            .locales
                            .get("en")
                            .unwrap()
                            .name
                            .to_lowercase(),
                        ' ',
                        '-',
                    );

                    let keyboard_plist_template = keyboard_path.join(INFO_PLIST);
                    let current_layout_path = keyboard_path.join(keyboard_folder_name);

                    std::fs::create_dir_all(&current_layout_path).unwrap();

                    let ios_keyboard_settings = IosSettings {
                        display_name: layout.autonym().to_string(),
                        short_version: target.version.clone(),
                        build_version: target.build.clone(),
                        package_id: target.package_id.clone(),
                        primary_language: language_tag.to_string(),
                        keyboard_index: layout_index,
                    };

                    // KEYBOARD PLIST
                    let layout_info_plist_path = current_layout_path.join(INFO_PLIST);
                    generate_keyboard_plist(
                        keyboard_plist_template,
                        ios_keyboard_settings.clone(),
                        layout_info_plist_path,
                    );

                    // HOSTING APP PLIST
                    let hosting_app_plist_path = hosting_app_path.join(INFO_PLIST);
                    generate_hosting_plist(hosting_app_plist_path, ios_keyboard_settings);

                    // NEW ENTITLEMENTS
                    let new_entitlements = format!("{}.{}", "group", target.package_id);

                    // UPDATE KEYBOARD ENTITLEMENTS
                    let keyboard_entitlements_path =
                        keyboard_path.join(format!("{}{}", KEYBOARD, ENTITLEMENTS_EXTENSION));
                    update_entitlements(keyboard_entitlements_path, vec![new_entitlements.clone()]);

                    // UPDATE HOSTING APP ENTITLEMENTS
                    let hosting_app_entitlements_path =
                        hosting_app_path.join(format!("{}{}", HOSTING_APP, ENTITLEMENTS_EXTENSION));
                    update_entitlements(
                        hosting_app_entitlements_path,
                        vec![new_entitlements.clone()],
                    );

                    // UPDATE ENTITLEMENTS IN SETTINGS BUNDLE PLIST
                    let root_plist_path = hosting_app_path.join(SETTINGS_BUNDLE).join(ROOT_PLIST);
                    let mut root_plist: SettingsRootDict =
                        plist::from_file(root_plist_path.clone()).expect("valid stuff");
                    root_plist.application_group_container_identifier = new_entitlements.clone();
                    plist::to_file_xml(root_plist_path.clone(), &root_plist).unwrap();
                }
            }
        }
    }
}
<commit_msg>rename struct<commit_after>use std::{
    cmp::Ordering,
    fmt,
    path::{Path, PathBuf},
};

use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use crate::{build::BuildStep, bundle::KbdgenBundle};

const REPOSITORY: &str = "repo";
const HOSTING_APP: &str = "HostingApp";
const HOSTING_INFO_STRINGS: &str = "InfoPlist.strings";

const KEYBOARD: &str = "Keyboard";
const INFO_PLIST: &str = "Info.plist";

const SETTINGS_BUNDLE: &str = "Settings.bundle";
const ROOT_PLIST: &str = "Root.plist";
const ENTITLEMENTS_EXTENSION: &str = ".entitlements";

#[derive(Serialize, Deserialize, Debug)]
pub struct XcodeHostingInfoStrings {
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
}

impl fmt::Display for XcodeHostingInfoStrings {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&format!(
            "\"CFBundleName\" = {:?};\n\"CFBundleDisplayName\" = {:?};",
            self.cf_bundle_name, self.cf_bundle_display_name
        ))
    }
}

// LAYOUT PLIST START

#[derive(Serialize, Deserialize, Debug)]
pub struct LayoutInfoPlistExtensionAttributes {
    #[serde(rename = "IsASCIICapable")]
    is_ascii_capable: bool,
    #[serde(rename = "PrefersRightToLeft")]
    prefers_right_to_left: bool,
    #[serde(rename = "PrimaryLanguage")]
    primary_language: String,
    #[serde(rename = "RequestsOpenAccess")]
    requests_open_access: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct KeyboardInfoPlistExtension {
    #[serde(rename = "NSExtensionAttributes")]
    ns_extension_attributes: LayoutInfoPlistExtensionAttributes,
    #[serde(rename = "NSExtensionPointIdentifier")]
    ns_extension_point_identifier: String,
    #[serde(rename = "NSExtensionPrincipalClass")]
    ns_extension_principal_class: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct KeyboardInfoPlist {
    #[serde(rename = "DivvunSpellerPath")]
    divvun_speller_path: String,
    #[serde(rename = "DivvunSpellerPackageKey")]
    divvun_speller_package_key: String,
    #[serde(rename = "CFBundleDevelopmentRegion")]
    cf_bundle_development_region: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
    #[serde(rename = "CFBundleExecutable")]
    cf_bundle_executable: String,
    #[serde(rename = "CFBundleIdentifier")]
    cf_bundle_identifier: String,
    #[serde(rename = "CFBundleInfoDictionaryVersion")]
    cf_bundle_info_dictionary_version: String,
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundlePackageType")]
    cf_bundle_package_type: String,
    #[serde(rename = "CFBundleShortVersionString")]
    cf_bundle_short_version_string: String,
    #[serde(rename = "CFBundleSignature")]
    cf_bundle_signature: String,
    #[serde(rename = "CFBundleVersion")]
    cf_bundle_version: String,
    #[serde(rename = "DivvunKeyboardIndex")]
    divvun_keyboard_index: usize,
    #[serde(rename = "ITSAppUsesNonExemptEncryption")]
    its_app_uses_non_exempt_encryption: bool,
    #[serde(rename = "LSApplicationQueriesSchemes")]
    ls_application_queries_schemes: Vec<String>,
    #[serde(rename = "NSExtension")]
    ns_extension: KeyboardInfoPlistExtension,
}

// LAYOUT PLIST END

#[derive(Serialize, Deserialize, Debug)]
pub struct EntitlementsDict {
    #[serde(rename = "com.apple.security.application-groups")]
    com_apple_security_application_groups: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PreferenceSpecifier {
    #[serde(rename = "Type")]
    preference_type: String,
    #[serde(rename = "Title")]
    title: String,
    #[serde(rename = "Key")]
    key: String,
    #[serde(rename = "DefaultValue")]
    default_value: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct SettingsRootDict {
    #[serde(rename = "StringsTable")]
    strings_table: String,
    #[serde(rename = "PreferenceSpecifiers")]
    preference_specifiers: Vec<PreferenceSpecifier>,
    #[serde(rename = "ApplicationGroupContainerIdentifier")]
    application_group_container_identifier: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct BundleSchemes {
    #[serde(rename = "CFBundleURLSchemes")]
    cf_bundle_url_schemes: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct HostingPlist {
    #[serde(rename = "CFBundleDevelopmentRegion")]
    cf_bundle_development_region: String,
    #[serde(rename = "CFBundleDisplayName")]
    cf_bundle_display_name: String,
    #[serde(rename = "CFBundleExecutable")]
    cf_bundle_executable: String,
    #[serde(rename = "CFBundleIdentifier")]
    cf_bundle_identifier: String,
    #[serde(rename = "CFBundleInfoDictionaryVersion")]
    cf_bundle_info_dictionary_version: String,
    #[serde(rename = "CFBundleName")]
    cf_bundle_name: String,
    #[serde(rename = "CFBundlePackageType")]
    cf_bundle_package_type: String,
    #[serde(rename = "CFBundleShortVersionString")]
    cf_bundle_short_version_string: String,
    #[serde(rename = "CFBundleSignature")]
    cf_bundle_signature: String,
    #[serde(rename = "CFBundleURLTypes")]
    cf_bundle_url_types: Vec<BundleSchemes>,
    #[serde(rename = "CFBundleVersion")]
    cf_bundle_version: String,
    #[serde(rename = "ITSAppUsesNonExemptEncryption")]
    its_app_uses_non_exempt_encryption: bool,
    #[serde(rename = "LSApplicationQueriesSchemes")]
    ls_application_queries_schemes: Vec<String>,
    #[serde(rename = "LSRequiresIPhoneOS")]
    ls_requires_iphone_os: bool,
    #[serde(rename = "UIBackgroundModes")]
    ui_background_modes: Vec<String>,
    #[serde(rename = "UILaunchStoryboardName")]
    ui_launch_storyboard_name: String,
    #[serde(rename = "UIRequiredDeviceCapabilities")]
    ui_required_device_capabilities: Vec<String>,
    #[serde(rename = "UISupportedInterfaceOrientations")]
    ui_supported_interface_orientations: Vec<String>,
    #[serde(rename = "UIUserInterfaceStyle")]
    ui_user_interface_style: String,
}

pub fn replace_all_occurances(input: String, character: char, replace_with: char) -> String {
    input
        .as_str()
        .chars()
        .map(|curr| {
            if curr.cmp(&character) != Ordering::Equal {
                curr
            } else {
                replace_with
            }
        })
        .into_iter()
        .collect::<String>()
}

pub fn generate_keyboard_plist(
    template_path: PathBuf,
    value: IosKeyboardSettings,
    output_path: PathBuf,
) {
    let mut keyboard_plist: KeyboardInfoPlist =
        plist::from_file(template_path.clone()).expect("valid stuff");

    keyboard_plist.cf_bundle_display_name = value.display_name;
    keyboard_plist.cf_bundle_short_version_string = value.short_version;
    keyboard_plist.cf_bundle_version = value.build_version;
    keyboard_plist.ls_application_queries_schemes[0] = value.package_id;
    keyboard_plist
        .ns_extension
        .ns_extension_attributes
        .primary_language = value.primary_language;
    keyboard_plist.divvun_keyboard_index = value.keyboard_index;

    plist::to_file_xml(output_path, &keyboard_plist).unwrap();
}

pub fn generate_hosting_plist(in_out_path: PathBuf, value: IosKeyboardSettings) {
    let mut hosting_app_plist: HostingPlist =
        plist::from_file(in_out_path.clone()).expect("valid stuff");

    hosting_app_plist.cf_bundle_display_name = value.display_name;
    hosting_app_plist.cf_bundle_short_version_string = value.short_version;
    hosting_app_plist.cf_bundle_version = value.build_version;
    hosting_app_plist.cf_bundle_url_types[0].cf_bundle_url_schemes[0] = value.package_id.clone();
    hosting_app_plist.ls_application_queries_schemes[0] = value.package_id;

    plist::to_file_xml(in_out_path, &hosting_app_plist).unwrap();
}

pub fn update_entitlements(entitlements_path: PathBuf, new_entitlements: Vec<String>) {
    let mut keyboard_entitlements: EntitlementsDict =
        plist::from_file(entitlements_path.clone()).expect("valid stuff");
    keyboard_entitlements.com_apple_security_application_groups = new_entitlements;
    plist::to_file_xml(entitlements_path, &keyboard_entitlements).unwrap();
}

#[derive(Clone)]
pub struct IosKeyboardSettings {
    display_name: String,
    short_version: String,
    build_version: String,
    package_id: String,
    primary_language: String,
    keyboard_index: usize,
}

pub struct GenerateXcode;

#[async_trait(?Send)]
impl BuildStep for GenerateXcode {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let repository_path = output_path.join(REPOSITORY);
        let hosting_app_path = repository_path.join(HOSTING_APP);
        let keyboard_path = repository_path.join(KEYBOARD);

        for (layout_index, (language_tag, layout)) in bundle.layouts.iter().enumerate() {
            if let Some(target) = &bundle.targets.ios {
                if let Some(_ios_layout) = &layout.i_os {
                    // GENERATE LOCALES
                    for (locale_name, locale_info) in &bundle.project.locales {
                        let locale_name = if locale_name == "en" {
                            "Base"
                        } else {
                            locale_name
                        };
                        let locale_path = hosting_app_path.join(&format!("{}.lproj", locale_name));
                        let info_path = locale_path.join(HOSTING_INFO_STRINGS);
                        println!("{:?}", locale_path);

                        std::fs::create_dir_all(&locale_path).unwrap();

                        let info_strings = XcodeHostingInfoStrings {
                            cf_bundle_name: locale_info.name.to_string(),
                            cf_bundle_display_name: locale_info.name.to_string(),
                        };
                        std::fs::write(info_path, info_strings.to_string()).unwrap();
                    }

                    // KEYBOARD PLIST
                    let keyboard_folder_name = replace_all_occurances(
                        bundle
                            .project
                            .locales
                            .get("en")
                            .unwrap()
                            .name
                            .to_lowercase(),
                        ' ',
                        '-',
                    );

                    let keyboard_plist_template = keyboard_path.join(INFO_PLIST);
                    let current_layout_path = keyboard_path.join(keyboard_folder_name);

                    std::fs::create_dir_all(&current_layout_path).unwrap();

                    let ios_keyboard_settings = IosKeyboardSettings {
                        display_name: layout.autonym().to_string(),
                        short_version: target.version.clone(),
                        build_version: target.build.clone(),
                        package_id: target.package_id.clone(),
                        primary_language: language_tag.to_string(),
                        keyboard_index: layout_index,
                    };

                    // KEYBOARD PLIST
                    let layout_info_plist_path = current_layout_path.join(INFO_PLIST);
                    generate_keyboard_plist(
                        keyboard_plist_template,
                        ios_keyboard_settings.clone(),
                        layout_info_plist_path,
                    );

                    // HOSTING APP PLIST
                    let hosting_app_plist_path = hosting_app_path.join(INFO_PLIST);
                    generate_hosting_plist(hosting_app_plist_path, ios_keyboard_settings);

                    // NEW ENTITLEMENTS
                    let new_entitlements = format!("{}.{}", "group", target.package_id);

                    // UPDATE KEYBOARD ENTITLEMENTS
                    let keyboard_entitlements_path =
                        keyboard_path.join(format!("{}{}", KEYBOARD, ENTITLEMENTS_EXTENSION));
                    update_entitlements(keyboard_entitlements_path, vec![new_entitlements.clone()]);

                    // UPDATE HOSTING APP ENTITLEMENTS
                    let hosting_app_entitlements_path =
                        hosting_app_path.join(format!("{}{}", HOSTING_APP, ENTITLEMENTS_EXTENSION));
                    update_entitlements(
                        hosting_app_entitlements_path,
                        vec![new_entitlements.clone()],
                    );

                    // UPDATE ENTITLEMENTS IN SETTINGS BUNDLE PLIST
                    let root_plist_path = hosting_app_path.join(SETTINGS_BUNDLE).join(ROOT_PLIST);
                    let mut root_plist: SettingsRootDict =
                        plist::from_file(root_plist_path.clone()).expect("valid stuff");
                    root_plist.application_group_container_identifier = new_entitlements.clone();
                    plist::to_file_xml(root_plist_path.clone(), &root_plist).unwrap();
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::ffi::CString;
use std::ptr;

use OomError;
use VK_ENTRY;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, OomError> {
                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(VK_ENTRY.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(VK_ENTRY.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> $sname {
                $sname::supported_raw().unwrap()
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
}

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<commit_msg>fixed dumb mistake<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::ffi::CString;
use std::ptr;

use OomError;
use VK_ENTRY;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, OomError> {
                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(VK_ENTRY.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(VK_ENTRY.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> $sname {
                $sname::supported_by_core_raw().unwrap()
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
}

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod msg;
pub mod buffer;
pub mod codec;
pub mod error;

use std::io;

use std::net::ToSocketAddrs;

use futures::{StartSend, Async, AsyncSink, Poll};
use futures::future::Future;
use futures::stream::Stream;
use futures::sink::Sink;

use tokio_core::net::TcpStream;
use tokio_core::reactor::Core as TokioCore;
use tokio_core::reactor::Handle as TokioCoreHandle;

use tokio_io::AsyncRead;

use futures_ex::StreamEx;

use net::arrow::proto::codec::ArrowCodec;
use net::arrow::proto::error::ArrowError;
use net::arrow::proto::msg::ArrowMessage;

/// Currently supported version of the Arrow protocol.
pub const ARROW_PROTOCOL_VERSION: u8 = 1;

/// Arrow Client implementation.
struct ArrowClient {
    tc_handle: TokioCoreHandle,
}

impl ArrowClient {
    /// Create a new Arrow Client.
    fn new(tc_handle: TokioCoreHandle) -> ArrowClient {
        ArrowClient {
            tc_handle: tc_handle,
        }
    }
}

impl Sink for ArrowClient {
    type SinkItem  = ArrowMessage;
    type SinkError = ArrowError;

    fn start_send(&mut self, _: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        // TODO: process a given message
        Ok(AsyncSink::Ready)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        Ok(Async::Ready(()))
    }
}

impl Stream for ArrowClient {
    type Item  = ArrowMessage;
    type Error = ArrowError;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        // TODO: return any messages that are ready to be sent
        Ok(Async::Ready(None))
    }
}

/// Connect Arrow Client to a given address.
pub fn connect(addr: &str) -> Result<(), ArrowError> {
    let mut core = TokioCore::new()?;

    let addr = addr.to_socket_addrs()?
        .next()
        .ok_or(io::Error::new(io::ErrorKind::Other, "unable to resolve a given address"))?;

    let aclient = ArrowClient::new(core.handle());

    let client = TcpStream::connect(&addr, &core.handle())
        .map_err(|err| ArrowError::from(err))
        .and_then(|stream| {
            let framed = stream.framed(ArrowCodec);
            let (sink, stream) = framed.split();

            let messages = stream.pipe(aclient);

            sink.send_all(messages)
                .and_then(|_| {
                    Ok(())
                })
        });

    core.run(client)
}
<commit_msg>Implementation of the Stream part of ArrowClient<commit_after>// Copyright 2017 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod msg;
pub mod buffer;
pub mod codec;
pub mod error;

use std::io;

use std::net::ToSocketAddrs;
use std::collections::VecDeque;

use futures::{StartSend, Async, AsyncSink, Poll};
use futures::future::Future;
use futures::stream::Stream;
use futures::sink::Sink;

use tokio_core::net::TcpStream;
use tokio_core::reactor::Core as TokioCore;
use tokio_core::reactor::Handle as TokioCoreHandle;

use tokio_io::AsyncRead;

use futures_ex::StreamEx;

use net::arrow::proto::codec::ArrowCodec;
use net::arrow::proto::error::ArrowError;
use net::arrow::proto::msg::ArrowMessage;

/// Currently supported version of the Arrow protocol.
pub const ARROW_PROTOCOL_VERSION: u8 = 1;

/// Arrow Client implementation.
struct ArrowClient {
    tc_handle: TokioCoreHandle,
    messages:  VecDeque<ArrowMessage>,
    closed:    bool,
}

impl ArrowClient {
    /// Create a new Arrow Client.
    fn new(tc_handle: TokioCoreHandle) -> ArrowClient {
        ArrowClient {
            tc_handle: tc_handle,
            messages:  VecDeque::new(),
            closed:    false,
        }
    }
}

impl Sink for ArrowClient {
    type SinkItem  = ArrowMessage;
    type SinkError = ArrowError;

    fn start_send(&mut self, _: ArrowMessage) -> StartSend<ArrowMessage, ArrowError> {
        // TODO: process a given message
        Ok(AsyncSink::Ready)
    }

    fn poll_complete(&mut self) -> Poll<(), ArrowError> {
        Ok(Async::Ready(()))
    }
}

impl Stream for ArrowClient {
    type Item  = ArrowMessage;
    type Error = ArrowError;

    fn poll(&mut self) -> Poll<Option<ArrowMessage>, ArrowError> {
        if self.closed {
            Ok(Async::Ready(None))
        } else if let Some(msg) = self.messages.pop_front() {
            Ok(Async::Ready(Some(msg)))
        } else {
            Ok(Async::NotReady)
        }
    }
}

/// Connect Arrow Client to a given address.
pub fn connect(addr: &str) -> Result<(), ArrowError> {
    let mut core = TokioCore::new()?;

    let addr = addr.to_socket_addrs()?
        .next()
        .ok_or(io::Error::new(io::ErrorKind::Other, "unable to resolve a given address"))?;

    let aclient = ArrowClient::new(core.handle());

    let client = TcpStream::connect(&addr, &core.handle())
        .map_err(|err| ArrowError::from(err))
        .and_then(|stream| {
            let framed = stream.framed(ArrowCodec);
            let (sink, stream) = framed.split();

            let messages = stream.pipe(aclient);

            sink.send_all(messages)
                .and_then(|_| {
                    Ok(())
                })
        });

    core.run(client)
}
<|endoftext|>


<commit_before>use bot::BotEnv;
use filters::Filter;

use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;

use serenity::prelude::Mentionable;
use serenity::model::guild::{Role, Member};
use serenity::model::channel::ReactionType;

use rand::{thread_rng, Rng};

use std::sync::Arc;

const USAGE: &'static str = "!randomreaction <channel/id> <message id> <emoji> [filters]";

pub struct RandomReactionCommand;

impl RandomReactionCommand {
  pub fn new(_: Arc<BotEnv>) -> Self {
    RandomReactionCommand
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  channel: ChannelOrId,
  message_id: u64,
  emoji: String,
  filters: Option<Vec<String>>
}

impl HasParams for RandomReactionCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for RandomReactionCommand {
  fn run(&self, _: &Context, _: &Message, guild: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    // FIXME: support > 100
    let reactions = params.channel.reaction_users(params.message_id, ReactionType::Unicode(params.emoji), Some(100), None)
      .map_err(|_| into!(CommandFailure, "Could not get reactions."))?;
    let filters: Option<Vec<Filter>> = params.filters.unwrap_or_default().iter().map(|x| Filter::parse(x)).collect();
    let filters = match filters {
      Some(f) => f,
      None => return Err("Invalid filters.".into())
    };
    let guild = match guild.find() {
      Some(g) => g,
      None => return Err("The guild must be cached.".into())
    };
    // FIXME: do less cloning
    let roles: Vec<Role> = guild.read().roles.values().cloned().collect();
    let members: Vec<Member> = guild.read().members.values().cloned().collect();
    let members: Vec<&Member> = reactions.into_iter()
      .map(|u| members.iter().find(|m| m.user.read().id == u.id))
      .filter(Option::is_some)
      .map(Option::unwrap)
      .filter(|m| filters.iter().all(|f| f.matches(m, &roles)))
      .collect();
    if members.is_empty() {
      return Err("No reactions matched those criteria.".into());
    }
    if members.len() == 1 {
      return Ok(format!("Only {} matched those criteria!", members[0].mention()).into());
    }
    let member = match thread_rng().choose(&members) {
      Some(u) => u,
      None => return Err("Could not randomly choose a reaction.".into())
    };
    Ok(format!("I've randomly selected {}!", member.mention()).into())
  }
}
<commit_msg>refactor: simplify reaction<commit_after>use bot::BotEnv;
use filters::Filter;

use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;

use serenity::prelude::Mentionable;
use serenity::model::guild::{Role, Member};

use rand::{thread_rng, Rng};

use std::sync::Arc;

const USAGE: &'static str = "!randomreaction <channel/id> <message id> <emoji> [filters]";

pub struct RandomReactionCommand;

impl RandomReactionCommand {
  pub fn new(_: Arc<BotEnv>) -> Self {
    RandomReactionCommand
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  channel: ChannelOrId,
  message_id: u64,
  emoji: String,
  filters: Option<Vec<String>>
}

impl HasParams for RandomReactionCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for RandomReactionCommand {
  fn run(&self, _: &Context, _: &Message, guild: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    // FIXME: support > 100
    let reactions = params.channel.reaction_users(params.message_id, params.emoji, Some(100), None)
      .map_err(|_| into!(CommandFailure, "Could not get reactions."))?;
    let filters: Option<Vec<Filter>> = params.filters.unwrap_or_default().iter().map(|x| Filter::parse(x)).collect();
    let filters = match filters {
      Some(f) => f,
      None => return Err("Invalid filters.".into())
    };
    let guild = match guild.find() {
      Some(g) => g,
      None => return Err("The guild must be cached.".into())
    };
    // FIXME: do less cloning
    let roles: Vec<Role> = guild.read().roles.values().cloned().collect();
    let members: Vec<Member> = guild.read().members.values().cloned().collect();
    let members: Vec<&Member> = reactions.into_iter()
      .map(|u| members.iter().find(|m| m.user.read().id == u.id))
      .filter(Option::is_some)
      .map(Option::unwrap)
      .filter(|m| filters.iter().all(|f| f.matches(m, &roles)))
      .collect();
    if members.is_empty() {
      return Err("No reactions matched those criteria.".into());
    }
    if members.len() == 1 {
      return Ok(format!("Only {} matched those criteria!", members[0].mention()).into());
    }
    let member = match thread_rng().choose(&members) {
      Some(u) => u,
      None => return Err("Could not randomly choose a reaction.".into())
    };
    Ok(format!("I've randomly selected {}!", member.mention()).into())
  }
}
<|endoftext|>


<commit_before>//! Generate binary emission code for each ISA.

use cranelift_entity::EntityRef;

use crate::error;
use crate::srcgen::Formatter;

use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes};

/// Generate code to handle a single recipe.
///
/// - Unpack the instruction data, knowing the format.
/// - Determine register locations for operands with register constraints.
/// - Determine stack slot locations for operands with stack constraints.
/// - Call hand-written code for the actual emission.
fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Formatter) {
    let inst_format = formats.get(recipe.format);
    let num_value_ops = inst_format.num_value_operands;

    let want_args = recipe.operands_in.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });
    assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);

    let want_outs = recipe.operands_out.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });

    let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name);

    // Unpack the instruction data.
    fmtln!(fmt, "if let &InstructionData::{} {{", inst_format.name);
    fmt.indent(|fmt| {
        fmt.line("opcode,");
        for f in &inst_format.imm_fields {
            fmtln!(fmt, "{},", f.member);
        }
        if want_args {
            if inst_format.has_value_list || num_value_ops > 1 {
                fmt.line("ref args,");
            } else {
                fmt.line("arg,");
            }
        }
        fmt.line("..");

        fmt.outdented_line("} = data {");

        // Pass recipe arguments in this order: inputs, imm_fields, outputs.
        let mut args = String::new();

        if want_args && !is_regmove {
            if inst_format.has_value_list {
                fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
            } else if num_value_ops == 1 {
                fmt.line("let args = [arg];");
            }
            args += &unwrap_values(&recipe.operands_in, "in", "args", fmt);
        }

        for f in &inst_format.imm_fields {
            args += &format!(", {}", f.member);
        }

        // Unwrap interesting output arguments.
        if want_outs {
            if recipe.operands_out.len() == 1 {
                fmt.line("let results = [func.dfg.first_result(inst)];")
            } else {
                fmt.line("let results = func.dfg.inst_results(inst);");
            }
            args += &unwrap_values(&recipe.operands_out, "out", "results", fmt);
        }

        // Optimization: Only update the register diversion tracker for regmove instructions.
        if is_regmove {
            fmt.line("divert.apply(data);")
        }

        match &recipe.emit {
            Some(emit) => {
                fmt.multi_line(emit);
                fmt.line("return;");
            }
            None => {
                fmtln!(
                    fmt,
                    "return recipe_{}(func, inst, sink, bits{});",
                    recipe.name.to_lowercase(),
                    args
                );
            }
        }
    });
    fmt.line("}");
}

/// Emit code that unwraps values living in registers or stack slots.
///
/// :param args: Input or output constraints.
/// :param prefix: Prefix to be used for the generated local variables.
/// :param values: Name of slice containing the values to be unwrapped.
/// :returns: Comma separated list of the generated variables
fn unwrap_values(
    args: &[OperandConstraint],
    prefix: &str,
    values_slice: &str,
    fmt: &mut Formatter,
) -> String {
    let mut varlist = String::new();
    for (i, cst) in args.iter().enumerate() {
        match cst {
            OperandConstraint::RegClass(_reg_class) => {
                let v = format!("{}_reg{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(
                    fmt,
                    "let {} = divert.reg({}[{}], &func.locations);",
                    v,
                    values_slice,
                    i
                );
            }
            OperandConstraint::Stack(stack) => {
                let v = format!("{}_stk{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(fmt, "let {} = StackRef::masked(", v);
                fmt.indent(|fmt| {
                    fmtln!(
                        fmt,
                        "divert.stack({}[{}], &func.locations),",
                        values_slice,
                        i
                    );
                    fmt.line(format!("{}, ", stack.stack_base_mask()));
                    fmt.line("&func.stack_slots,");
                });
                fmt.line(").unwrap();");
            }
            _ => {}
        }
    }
    varlist
}

fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) {
    fmt.doc_comment(format!(
        "Emit binary machine code for `inst` for the {} ISA.",
        isa_name
    ));

    if recipes.is_empty() {
        fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
        fmt.indent(|fmt| {
            fmt.line("func: &Function,");
            fmt.line("inst: Inst,");
            fmt.line("_divert: &mut RegDiversions,");
            fmt.line("_sink: &mut CS,");
        });
        fmt.line(") {");
        fmt.indent(|fmt| {
            // No encoding recipes: Emit a stub.
            fmt.line("bad_encoding(func, inst)");
        });
        fmt.line("}");
        return;
    }

    fmt.line("#[allow(unused_variables, unreachable_code)]");
    fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
    fmt.indent(|fmt| {
        fmt.line("func: &Function,");
        fmt.line("inst: Inst,");
        fmt.line("divert: &mut RegDiversions,");
        fmt.line("sink: &mut CS,");
    });

    fmt.line(") {");
    fmt.indent(|fmt| {
        fmt.line("let encoding = func.encodings[inst];");
        fmt.line("let bits = encoding.bits();");
        fmt.line("let data = &func.dfg[inst];");
        fmt.line("match encoding.recipe() {");
        fmt.indent(|fmt| {
            for (i, recipe) in recipes.iter() {
                fmt.comment(format!("Recipe {}", recipe.name));
                fmtln!(fmt, "{} => {{", i.index());
                fmt.indent(|fmt| {
                    gen_recipe(formats, recipe, fmt);
                });
                fmt.line("}");
            }
            fmt.line("_ => {},");
        });
        fmt.line("}");

        // Allow for unencoded ghost instructions. The verifier will check details.
        fmt.line("if encoding.is_legal() {");
        fmt.indent(|fmt| {
            fmt.line("bad_encoding(func, inst);");
        });
        fmt.line("}");
    });
    fmt.line("}");
}

pub fn generate(
    formats: &FormatRegistry,
    isa_name: &str,
    recipes: &Recipes,
    binemit_filename: &str,
    out_dir: &str,
) -> Result<(), error::Error> {
    let mut fmt = Formatter::new();
    gen_isa(formats, isa_name, recipes, &mut fmt);
    fmt.update_file(binemit_filename, out_dir)?;
    Ok(())
}
<commit_msg>Rename data to inst_data in binemit generated code.<commit_after>//! Generate binary emission code for each ISA.

use cranelift_entity::EntityRef;

use crate::error;
use crate::srcgen::Formatter;

use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes};

/// Generate code to handle a single recipe.
///
/// - Unpack the instruction data, knowing the format.
/// - Determine register locations for operands with register constraints.
/// - Determine stack slot locations for operands with stack constraints.
/// - Call hand-written code for the actual emission.
fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Formatter) {
    let inst_format = formats.get(recipe.format);
    let num_value_ops = inst_format.num_value_operands;

    let want_args = recipe.operands_in.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });
    assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);

    let want_outs = recipe.operands_out.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });

    let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name);

    // Unpack the instruction data.
    fmtln!(fmt, "if let &InstructionData::{} {{", inst_format.name);
    fmt.indent(|fmt| {
        fmt.line("opcode,");
        for f in &inst_format.imm_fields {
            fmtln!(fmt, "{},", f.member);
        }
        if want_args {
            if inst_format.has_value_list || num_value_ops > 1 {
                fmt.line("ref args,");
            } else {
                fmt.line("arg,");
            }
        }
        fmt.line("..");

        fmt.outdented_line("} = inst_data {");

        // Pass recipe arguments in this order: inputs, imm_fields, outputs.
        let mut args = String::new();

        if want_args && !is_regmove {
            if inst_format.has_value_list {
                fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
            } else if num_value_ops == 1 {
                fmt.line("let args = [arg];");
            }
            args += &unwrap_values(&recipe.operands_in, "in", "args", fmt);
        }

        for f in &inst_format.imm_fields {
            args += &format!(", {}", f.member);
        }

        // Unwrap interesting output arguments.
        if want_outs {
            if recipe.operands_out.len() == 1 {
                fmt.line("let results = [func.dfg.first_result(inst)];")
            } else {
                fmt.line("let results = func.dfg.inst_results(inst);");
            }
            args += &unwrap_values(&recipe.operands_out, "out", "results", fmt);
        }

        // Optimization: Only update the register diversion tracker for regmove instructions.
        if is_regmove {
            fmt.line("divert.apply(inst_data);")
        }

        match &recipe.emit {
            Some(emit) => {
                fmt.multi_line(emit);
                fmt.line("return;");
            }
            None => {
                fmtln!(
                    fmt,
                    "return recipe_{}(func, inst, sink, bits{});",
                    recipe.name.to_lowercase(),
                    args
                );
            }
        }
    });
    fmt.line("}");
}

/// Emit code that unwraps values living in registers or stack slots.
///
/// :param args: Input or output constraints.
/// :param prefix: Prefix to be used for the generated local variables.
/// :param values: Name of slice containing the values to be unwrapped.
/// :returns: Comma separated list of the generated variables
fn unwrap_values(
    args: &[OperandConstraint],
    prefix: &str,
    values_slice: &str,
    fmt: &mut Formatter,
) -> String {
    let mut varlist = String::new();
    for (i, cst) in args.iter().enumerate() {
        match cst {
            OperandConstraint::RegClass(_reg_class) => {
                let v = format!("{}_reg{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(
                    fmt,
                    "let {} = divert.reg({}[{}], &func.locations);",
                    v,
                    values_slice,
                    i
                );
            }
            OperandConstraint::Stack(stack) => {
                let v = format!("{}_stk{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(fmt, "let {} = StackRef::masked(", v);
                fmt.indent(|fmt| {
                    fmtln!(
                        fmt,
                        "divert.stack({}[{}], &func.locations),",
                        values_slice,
                        i
                    );
                    fmt.line(format!("{}, ", stack.stack_base_mask()));
                    fmt.line("&func.stack_slots,");
                });
                fmt.line(").unwrap();");
            }
            _ => {}
        }
    }
    varlist
}

fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) {
    fmt.doc_comment(format!(
        "Emit binary machine code for `inst` for the {} ISA.",
        isa_name
    ));

    if recipes.is_empty() {
        fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
        fmt.indent(|fmt| {
            fmt.line("func: &Function,");
            fmt.line("inst: Inst,");
            fmt.line("_divert: &mut RegDiversions,");
            fmt.line("_sink: &mut CS,");
        });
        fmt.line(") {");
        fmt.indent(|fmt| {
            // No encoding recipes: Emit a stub.
            fmt.line("bad_encoding(func, inst)");
        });
        fmt.line("}");
        return;
    }

    fmt.line("#[allow(unused_variables, unreachable_code)]");
    fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
    fmt.indent(|fmt| {
        fmt.line("func: &Function,");
        fmt.line("inst: Inst,");
        fmt.line("divert: &mut RegDiversions,");
        fmt.line("sink: &mut CS,");
    });

    fmt.line(") {");
    fmt.indent(|fmt| {
        fmt.line("let encoding = func.encodings[inst];");
        fmt.line("let bits = encoding.bits();");
        fmt.line("let inst_data = &func.dfg[inst];");
        fmt.line("match encoding.recipe() {");
        fmt.indent(|fmt| {
            for (i, recipe) in recipes.iter() {
                fmt.comment(format!("Recipe {}", recipe.name));
                fmtln!(fmt, "{} => {{", i.index());
                fmt.indent(|fmt| {
                    gen_recipe(formats, recipe, fmt);
                });
                fmt.line("}");
            }
            fmt.line("_ => {},");
        });
        fmt.line("}");

        // Allow for unencoded ghost instructions. The verifier will check details.
        fmt.line("if encoding.is_legal() {");
        fmt.indent(|fmt| {
            fmt.line("bad_encoding(func, inst);");
        });
        fmt.line("}");
    });
    fmt.line("}");
}

pub fn generate(
    formats: &FormatRegistry,
    isa_name: &str,
    recipes: &Recipes,
    binemit_filename: &str,
    out_dir: &str,
) -> Result<(), error::Error> {
    let mut fmt = Formatter::new();
    gen_isa(formats, isa_name, recipes, &mut fmt);
    fmt.update_file(binemit_filename, out_dir)?;
    Ok(())
}
<|endoftext|>