gladius/lib.rs
1//! # Gladius - High-Performance Typing Trainer Library
2//!
3//! Gladius is a comprehensive Rust library for building typing trainer applications.
4//! It provides real-time typing analysis, flexible rendering systems, and detailed
5//! performance statistics with a focus on accuracy, performance, and ease of use.
6//!
7//! ## Quick Start
8//!
9//! ```rust
10//! use gladius::TypingSession;
11//!
12//! // Create a typing session
13//! let mut session = TypingSession::new("Hello, world!").unwrap();
14//!
15//! // Process user input
16//! while let Some((char, result)) = session.input(Some('H')) {
17//! println!("Typed '{}': {:?}", char, result);
18//! break; // Just for demo
19//! }
20//!
21//! // Get progress and statistics
22//! println!("Progress: {:.1}%", session.completion_percentage());
23//! println!("WPM: {:.1}", session.statistics().measurements.last()
24//! .map(|m| m.wpm.raw).unwrap_or(0.0));
25//! ```
26//!
27//! ## Key Features
28//!
29//! ### ๐ **High Performance**
30//! - **Fast character processing** - Amortized O(1) keystroke handling
31//! - **O(1) word lookups** - Efficient character-to-word mapping
32//! - **Optimized statistics** - Welford's algorithm for numerical stability
33//! - **Memory efficient** - Minimal allocations during typing
34//!
35//! ### ๐ **Comprehensive Statistics**
36//! - **Words per minute** (raw, corrected, actual)
37//! - **Input per minute** (raw, actual)
38//! - **Accuracy percentages** (raw, actual)
39//! - **Consistency analysis** with standard deviation
40//! - **Detailed error tracking** by character and word
41//! - **Real-time measurements** at configurable intervals
42//!
43//! ### ๐ฏ **Flexible Rendering**
44//! - **Character-level rendering** with typing state information
45//! - **Line-based rendering** with intelligent word wrapping
46//! - **Cursor position tracking** across line boundaries
47//! - **Unicode support** for international characters and emojis
48//! - **Generic renderer interface** for any UI framework
49//!
50//! ### โ๏ธ **Configurable Behavior**
51//! - **Measurement intervals** for statistics collection
52//! - **Line wrapping options** (word boundaries vs. character wrapping)
53//! - **Newline handling** (respect or ignore paragraph breaks)
54//! - **Performance tuning** for different use cases
55//!
56//! ## Architecture Overview
57//!
58//! Gladius is built with a modular architecture where each component has a specific responsibility:
59//!
60#![doc = simple_mermaid::mermaid!("../diagrams/architecture_overview.mmd")]
61//!
62//! ## Core Modules
63//!
64//! | Module | Purpose | Key Types |
65//! |--------|---------|-----------|
66//! | [`session`] | Session coordination and main API | [`TypingSession`] |
67//! | [`buffer`] | Text storage and word/character management | [`Buffer`](buffer::Buffer) |
68//! | [`input_handler`] | Keystroke processing and validation | [`InputHandler`](input_handler::InputHandler) |
69//! | [`statistics`] | Performance data collection and analysis | [`Statistics`](statistics::Statistics), [`TempStatistics`](statistics::TempStatistics) |
70//! | [`statistics_tracker`] | Real-time statistics coordination | [`StatisticsTracker`](statistics_tracker::StatisticsTracker) |
71//! | [`render`] | Text display and line management | [`RenderingContext`](render::RenderingContext), [`LineContext`](render::LineContext) |
72//! | [`math`] | Performance calculation algorithms | [`Wpm`](math::Wpm), [`Accuracy`](math::Accuracy), [`Consistency`](math::Consistency) |
73//! | [`config`] | Runtime behavior configuration | [`Configuration`](config::Configuration) |
74//!
75//! ## Usage Examples
76//!
77//! ### Basic Typing Session
78//!
79//! ```rust
80//! use gladius::TypingSession;
81//! use gladius::CharacterResult;
82//!
83//! let mut session = TypingSession::new("The quick brown fox").unwrap();
84//!
85//! // Process typing input
86//! match session.input(Some('T')) {
87//! Some((ch, CharacterResult::Correct)) => println!("Correct: {}", ch),
88//! Some((ch, CharacterResult::Wrong)) => println!("Wrong: {}", ch),
89//! Some((ch, CharacterResult::Corrected)) => println!("Corrected: {}", ch),
90//! Some((ch, CharacterResult::Deleted(state))) => println!("Deleted: {} (was {:?})", ch, state),
91//! None => println!("No input processed"),
92//! }
93//! ```
94//!
95//! ### Custom Configuration
96//!
97//! ```rust
98//! use gladius::{TypingSession, config::Configuration};
99//!
100//! let config = Configuration {
101//! measurement_interval_seconds: 0.5, // More frequent measurements
102//! };
103//!
104//! let session = TypingSession::new("Hello, world!")
105//! .unwrap()
106//! .with_configuration(config);
107//! ```
108//!
109//! ### Character-level Rendering
110//!
111//! ```rust
112//! use gladius::TypingSession;
113//!
114//! let session = TypingSession::new("hello").unwrap();
115//!
116//! let rendered: Vec<String> = session.render(|ctx| {
117//! let cursor = if ctx.has_cursor { " |" } else { "" };
118//! let state = match ctx.character.state {
119//! gladius::State::Correct => "โ",
120//! gladius::State::Wrong => "โ",
121//! gladius::State::None => "ยท",
122//! _ => "?",
123//! };
124//! format!("{}{}{}", ctx.character.char, state, cursor)
125//! });
126//! ```
127//!
128//! ### Line-based Rendering
129//!
130//! ```rust
131//! use gladius::{TypingSession, render::LineRenderConfig};
132//!
133//! let session = TypingSession::new("The quick brown fox jumps over the lazy dog").unwrap();
134//! let config = LineRenderConfig::new(20).with_word_wrapping(false);
135//!
136//! let lines: Vec<String> = session.render_lines(|line_ctx| {
137//! Some(line_ctx.contents.iter()
138//! .map(|ctx| ctx.character.char)
139//! .collect())
140//! }, config);
141//!
142//! // Results in word-wrapped lines of ~20 characters each
143//! ```
144//!
145//! ### Complete Session with Statistics
146//!
147//! ```rust
148//! use gladius::{TypingSession, CharacterResult};
149//!
150//! let mut session = TypingSession::new("rust").unwrap();
151//! let text_chars = ['r', 'u', 's', 't'];
152//!
153//! // Type the complete text
154//! for ch in text_chars {
155//! session.input(Some(ch));
156//! }
157//!
158//! // Get final statistics
159//! if session.is_fully_typed() {
160//! let stats = session.finalize();
161//! println!("Final WPM: {:.1}", stats.wpm.raw);
162//! println!("Accuracy: {:.1}%", stats.accuracy.raw);
163//! println!("Total time: {:.2}s", stats.duration.as_secs_f64());
164//! println!("Character errors: {:?}", stats.counters.char_errors);
165//! }
166//! ```
167//!
168//! ## Performance Characteristics
169//!
170//! | Operation | Time Complexity | Notes |
171//! |-----------|----------------|-------|
172//! | Character input | O(1) amortized, O(w) worst case | Usually constant, worst case when recalculating word state |
173//! | Character lookup | O(1) | Direct vector indexing |
174//! | Word lookup | O(1) | Pre-computed mapping |
175//! | Statistics update | O(1) typical, O(m) when measuring | Most updates are constant, measurements scan history |
176//! | Rendering | O(n) | Linear in text length |
177//! | Line wrapping | O(n) with O(w) lookahead | Linear with word boundary lookahead |
178//! | Session creation | O(n) | One-time text parsing |
179//!
180//! ## Thread Safety
181//!
182//! Gladius types are not thread-safe by design for maximum performance. Each typing
183//! session should be used on a single thread. Multiple sessions can run concurrently
184//! on different threads.
185//!
186//! ## Memory Usage
187//!
188//! - **Text storage**: O(n) where n is text length
189//! - **Statistics history**: O(k) where k is number of measurements
190//! - **Input history**: O(m) where m is number of keystrokes
191//! - **Word mapping**: O(n) pre-computed character-to-word index
192//!
193//! Memory usage is optimized for typing trainer use cases with efficient data structures
194//! and minimal allocations during active typing.
195//!
196//! ## Minimum Supported Rust Version (MSRV)
197//!
198//! Gladius supports Rust 1.88.0 and later.
199
200pub mod buffer;
201pub mod config;
202pub mod input_handler;
203pub mod math;
204pub mod render;
205pub mod session;
206pub mod statistics;
207pub mod statistics_tracker;
208
209/// Re-export of the main entry point for convenient access
210pub use session::TypingSession;
211
212// Shared types for readability and type safety
213type Timestamp = f64;
214type Minutes = f64;
215type Float = f64;
216
217/// Represents the current typing state of a character or word
218///
219/// States have a specific ordering that reflects their priority for word state calculations.
220/// Higher priority states override lower priority ones when determining overall word state.
221///
222/// # State Transitions
223///
224/// ```text
225/// None โ Correct/Wrong โ Deleted โ Corrected (via new input)
226/// ```
227///
228/// # Examples
229///
230/// ```rust
231/// use gladius::State;
232///
233/// // Priority ordering (Higher states override lower ones)
234/// assert!(State::Wrong > State::Corrected);
235/// assert!(State::Corrected > State::Correct);
236/// assert!(State::Correct > State::None);
237/// ```
238#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
239pub enum State {
240 // == Pre delete or add ==
241 /// The text has never been touched
242 #[default]
243 None,
244
245 // The below are in a specific order to updating words properly
246
247 // == Post add ==
248 /// The text is correct
249 Correct,
250 /// The text was corrected
251 Corrected,
252 /// The text is wrong
253 Wrong,
254
255 // == Post delete ==
256 /// The text was correct, but has since been deleted
257 WasCorrect,
258 /// The text was corrected, but has since been deleted
259 WasCorrected,
260 /// The text was wrong, but has since been deleted or corrected
261 WasWrong,
262}
263
264/// Result of processing a character input during typing
265///
266/// Indicates what happened when a character was typed or deleted, providing
267/// detailed feedback about the typing action for statistics and UI updates.
268///
269/// # Ordering
270///
271/// Results are ordered by their impact on typing accuracy, with `Correct` being
272/// the best outcome and `Deleted` potentially indicating typing inefficiency.
273///
274/// # Examples
275///
276/// ```rust
277/// use gladius::{CharacterResult, State};
278///
279/// // Typing the correct character first time
280/// let result = CharacterResult::Correct;
281///
282/// // Typing wrong, then deleting and typing correctly
283/// let wrong = CharacterResult::Wrong;
284/// let deleted = CharacterResult::Deleted(State::Wrong);
285/// let corrected = CharacterResult::Corrected;
286/// ```
287#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
288pub enum CharacterResult {
289 /// A character was deleted from the input (contains the previous state)
290 Deleted(State),
291 /// Character was typed incorrectly (doesn't match expected character)
292 Wrong,
293 /// Character was typed correctly after being previously wrong (correction)
294 Corrected,
295 /// Character was typed correctly on the first attempt
296 Correct,
297}
298
299/// Represents a word in the text with its boundaries and typing state
300///
301/// Words are defined as sequences of non-whitespace characters separated by whitespace.
302/// Each word tracks its position in the text and its overall typing state based on
303/// the states of its constituent characters.
304///
305/// # Examples
306///
307/// ```rust
308/// use gladius::{Word, State};
309///
310/// let word = Word {
311/// start: 0, // First character index
312/// end: 4, // Last character index + 1 (exclusive)
313/// state: State::Correct,
314/// };
315///
316/// // Check if a character index is part of this word
317/// assert!(word.contains_index(&2)); // Character at index 2 is in the word
318/// assert!(!word.contains_index(&5)); // Character at index 5 is not in the word
319/// ```
320#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
321pub struct Word {
322 /// Starting character index (inclusive)
323 pub start: usize,
324 /// Ending character index (exclusive)
325 pub end: usize,
326 /// Current typing state of the word (highest priority state of any character)
327 pub state: State,
328}
329
330impl Word {
331 /// Check if a character index falls within this word's boundaries
332 ///
333 /// # Parameters
334 ///
335 /// * `index` - Character index to check
336 ///
337 /// # Returns
338 ///
339 /// `true` if the index is within [start, end), `false` otherwise
340 pub fn contains_index(&self, index: &usize) -> bool {
341 (self.start..self.end).contains(index)
342 }
343}
344
345/// Represents a single character in the text with its typing state
346///
347/// Characters are the fundamental unit of typing analysis. Each character
348/// maintains its Unicode value and current state based on user input.
349///
350/// # Examples
351///
352/// ```rust
353/// use gladius::{Character, State};
354///
355/// let char = Character {
356/// char: 'a',
357/// state: State::Correct,
358/// };
359///
360/// // Unicode characters are fully supported
361/// let unicode_char = Character {
362/// char: '๐',
363/// state: State::None,
364/// };
365/// ```
366#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
367pub struct Character {
368 /// The Unicode character
369 pub char: char,
370 /// Current typing state of this character
371 pub state: State,
372}