Skip to main content

oxeylyzer_core/
lib.rs

1//! Core logic for the oxeylyzer keyboard layout analyzer.
2//!
3//! This crate provides the necessary tools to analyze and generate keyboard layouts,
4//! including data structures for bigrams, trigrams, and layout evaluation.
5
6#![warn(missing_docs)]
7
8/// Data structures for analyzing layout performance.
9pub mod analyzer_data;
10/// Mapping between characters and internal byte representations.
11pub mod char_mapping;
12/// Tools for cleaning and processing corpus data.
13pub mod corpus_cleaner;
14/// Basic data structures for corpus information.
15pub mod data;
16/// Fast layout representation for optimization.
17pub mod fast_layout;
18/// Layout generation algorithms.
19pub mod generate;
20/// Layout representation and evaluation.
21pub mod layout;
22/// Trigram pattern analysis.
23pub mod trigram_patterns;
24/// Miscellaneous utility functions.
25pub mod utility;
26/// Weights for different layout metrics.
27pub mod weights;
28
29use std::path::{Path, PathBuf};
30
31pub use rayon;
32pub use serde;
33
34use thiserror::Error;
35
36/// Character used to replace unknown or invalid characters in a corpus.
37pub const REPLACEMENT_CHAR: char = char::REPLACEMENT_CHARACTER;
38/// Internal representation of a space character.
39pub const SPACE_CHAR: char = '␣';
40/// Internal representation of a shift key press.
41pub const SHIFT_CHAR: char = '⇑';
42/// Internal representation of a repeat key.
43pub const REPEAT_KEY: char = '↻';
44
45/// Errors that can occur within the oxeylyzer-core crate.
46#[derive(Debug, Error)]
47pub enum OxeylyzerError {
48    /// Encountered a bigram that does not have a length of 2.
49    #[error("Bigrams should contain 2 characters, bigram with length {0} encountered.")]
50    InvalidBigramLength(usize),
51    /// Encountered a trigram that does not have a length of 3.
52    #[error("Trigrams should contain 3 characters, trigram with length {0} encountered.")]
53    InvalidTrigramLength(usize),
54    /// Failed to initialize the file chunker for corpus processing.
55    #[error("Failed to create a file chunker")]
56    ChunkerInitError,
57    /// Failed to split the corpus into valid chunks.
58    #[error("Failed to create appropriate chunks")]
59    ChunkerChunkError,
60    /// The provided path is not a file or directory.
61    #[error("Path must be either a directory or a file, '{}' is neither", .0.display())]
62    NotAFile(PathBuf),
63    /// The specified path does not exist on the file system.
64    #[error("Cannot load data for '{}' as it does not exist.", .0.display())]
65    PathDoesNotExist(PathBuf),
66    /// No name was provided for the corpus data.
67    #[error("Specifying a name for the corpus is required")]
68    MissingDataName,
69    /// Could not serialize the processed data to JSON.
70    #[error("Failed to serialize data for language '{0}'")]
71    CouldNotSerializeData(String),
72    /// The corpus output path is invalid (usually missing .json extension).
73    #[error("Corpus path '{}' is invalid as it does not end in a (.json) file.", .0.display())]
74    InvalidCorpusPath(PathBuf),
75
76    /// Wrapper for general anyhow errors.
77    #[error("{0:#}")]
78    AnyhowError(#[from] anyhow::Error),
79
80    /// Wrapper for errors occurring in a WASM environment.
81    #[cfg(target_arch = "wasm32")]
82    #[error("{0}")]
83    GlooError(#[from] gloo_net::Error),
84}
85
86/// Result type used throughout the oxeylyzer-core crate.
87pub type Result<T> = std::result::Result<T, OxeylyzerError>;
88
89/// Extension trait to provide context to Result types, converting foreign errors to [`OxeylyzerError`].
90pub trait OxeylyzerResultExt<T> {
91    /// Adds a path as context to the error.
92    fn path_context<P: AsRef<Path>>(self, path: P) -> Result<T>;
93
94    /// Adds a string as context to the error.
95    fn str_context<S: ToString>(self, s: S) -> Result<T>;
96}
97
98impl<T, E> OxeylyzerResultExt<T> for std::result::Result<T, E>
99where
100    E: std::error::Error + Send + Sync + 'static,
101{
102    fn path_context<P: AsRef<Path>>(self, path: P) -> Result<T> {
103        use anyhow::Context;
104
105        self.context(path.as_ref().display().to_string())
106            .map_err(OxeylyzerError::AnyhowError)
107    }
108
109    fn str_context<S: ToString>(self, s: S) -> Result<T> {
110        use anyhow::Context;
111
112        self.context(s.to_string())
113            .map_err(OxeylyzerError::AnyhowError)
114    }
115}