Skip to content

Rust

Lipi Lekhika offers a high-performance, type-safe transliteration library for Rust. All script data is embedded at compile time for zero runtime overhead.

The crate is no_std compatible by default: it uses alloc and does not require the Rust standard library, which suits WASM, embedded, and other constrained targets.

The recommended way to install is using cargo add:

Terminal window
cargo add lipilekhika

The transliterate function is the primary API for converting text between scripts. It takes Script enum values and returns a Cow<str> directly (no Result).

use lipilekhika::{transliterate, Script};
fn main() {
let result = transliterate(
"na jAyatE mriyatE vA",
Script::Normal,
Script::Devanagari,
None
);
println!("{}", result); // न जायते म्रियते वा
}

Parameters:

  • text: &(impl AsRef<str> + ?Sized) — Text to transliterate
  • from: Script — Source script/language
  • to: Script — Target script/language
  • trans_options: Option<&CustomOptions> — Optional custom transliteration options

Returns: Cow<'a, str> — Transliterated text (borrows input when from == to)

The fourth argument accepts a CustomOptions value. The recommended way to construct one is with CustomOptionsBuilder: call CustomOptionsBuilder::default(), enable options with <category>_<option>(bool) setters (replace : in canonical keys with _), then .build().

use lipilekhika::{transliterate, CustomOptionsBuilder, Script};
fn main() {
let options = CustomOptionsBuilder::default()
.brahmic_to_brahmic_replace_pancham_varga_varna_with_anusvAra(true)
.build();
let result = transliterate(
"गङ्गा",
Script::Devanagari,
Script::Gujarati,
Some(&options),
);
println!("{}", result); // ગંગા (instead of ગઙ્ગા)
}

If your configuration already lives in a map (for example from JSON or another language binding), convert with CustomOptions::try_from_map and export with CustomOptions::to_options_map. Unknown keys return UnknownCustomOptionKey.

use lipilekhika::{transliterate, CustomOptions, Script};
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(
"brahmic_to_brahmic:replace_pancham_varga_varna_with_anusvAra".to_string(),
true,
);
let options = CustomOptions::try_from_map(&map).unwrap();
let result = transliterate("गङ्गा", Script::Devanagari, Script::Gujarati, Some(&options));
println!("{}", result);
let exported: HashMap<String, bool> = options.to_options_map();
}

All script/language parameters use the Script enum instead of raw strings. You can use full script names, language names, or shorthand aliases:

use lipilekhika::Script;
// Full script names
Script::Devanagari
Script::Telugu
Script::Normal // Romanized input scheme
// Language names (resolve to their script)
Script::Hindi // → Devanagari
Script::Sanskrit // → Devanagari
Script::English // → Normal
Script::Punjabi // → Gurumukhi
// Shorthand aliases
Script::Dev // → Devanagari
Script::Tel // → Telugu
Script::Tam // → Tamil
Script::Kan // → Kannada

Get the string representation using to_string():

use lipilekhika::Script;
let name = Script::Devanagari.to_string(); // "Devanagari"
let alias = Script::Dev.to_string(); // "dev"

Parse a &str into a Script using FromStr (case-insensitive):

use std::str::FromStr;
use lipilekhika::Script;
let script = Script::from_str("Devanagari").unwrap(); // Script::Devanagari
let script = Script::from_str("dev").unwrap(); // Script::Dev
let script = Script::from_str("hindi").unwrap(); // Script::Hindi

Get the normalized/resolved script using .into() to convert a Script (which may be a language or alias) into the underlying ScriptListEnum:

use lipilekhika::{Script, ScriptListEnum};
let resolved: ScriptListEnum = Script::Hindi.into(); // ScriptListEnum::Devanagari
let resolved: ScriptListEnum = Script::Dev.into(); // ScriptListEnum::Devanagari
let resolved: ScriptListEnum = Script::English.into(); // ScriptListEnum::Normal

Use get_all_options to discover which custom options are available for a specific script pair:

use lipilekhika::{get_all_options, Script};
fn main() {
let options = get_all_options(Script::Devanagari, Script::Telugu);
for option in options {
println!("Available option: {}", option);
}
}

Parameters:

  • from_script: Script — Source script
  • to_script: Script — Target script

Returns: Vec<String> — List of option keys

Use get_script_list_data() to grab the cached script metadata that powers the transliteration helpers. It returns a &'static ScriptListData containing the ordered script and language names plus the maps that translate between languages, scripts, and alternate aliases.

use lipilekhika::get_script_list_data;
fn main() {
let data = get_script_list_data();
println!("Supported scripts: {:?}", data.scripts);
println!("Language to script map: {:?}", data.lang_script_map);
}

Each call is zero-cost after the first because the data is stored in a OnceLock, so you can use it wherever you need to inspect available scripts or resolve language aliases.

Use get_script_typing_data_map to get typing mappings for a script (useful for building custom input methods):

use lipilekhika::{get_script_typing_data_map, Script};
fn main() {
let typing_data = get_script_typing_data_map(Script::Devanagari);
// Access common character mappings
for (text, list_type, mappings) in &typing_data.common_krama_map {
if !mappings.is_empty() {
println!("{} can be typed using: {:?}", text, mappings);
}
}
// Access script-specific character mappings
for (text, list_type, mappings) in &typing_data.script_specific_krama_map {
if !mappings.is_empty() {
println!("{} (script-specific) can be typed using: {:?}", text, mappings);
}
}
}

Parameters:

  • typing_script: Script — Script

Returns: ScriptTypingDataMap — Typing data containing:

  • common_krama_map — Mappings for common characters
  • script_specific_krama_map — Mappings for script-specific characters

Each mapping is a tuple of (text: String, type: ListType, input_mappings: Vec<String>).

For real-time character-by-character input, use the typing module which provides a stateful context for handling keyboard input.

  • take_key_input / take_key_input_char: With the crate std feature enabled, take_key_input_char records wall-clock time between keys and may call clear_context() when the idle period exceeds auto_context_clear_time_ms.
  • Without std: Timing fields are not compiled in; you must clear context explicitly (for example on focus loss or with your host’s own clock).
  • clear_context() is always available regardless of features.

Enable timed auto-clear in your Cargo.toml:

lipilekhika = { version = "1.1", features = ["std"] }
use lipilekhika::Script;
use lipilekhika::typing::{TypingContext, TypingContextOptions};
fn main() {
let mut ctx = TypingContext::new(Script::Devanagari, None);
// Process character-by-character input
let diff = ctx.take_key_input("n");
println!("Delete: {}, Add: '{}'", diff.to_delete_chars_count, diff.diff_add_text);
// Output: Delete: 0, Add: 'न्'
let diff = ctx.take_key_input("a");
println!("Delete: {}, Add: '{}'", diff.to_delete_chars_count, diff.diff_add_text);
// Output: Delete: 2, Add: 'न'
// Clear context when needed
ctx.clear_context();
}
use lipilekhika::Script;
use lipilekhika::typing::{TypingContext, TypingContextOptions};
fn main() {
let options = TypingContextOptions {
auto_context_clear_time_ms: 3000,
use_native_numerals: true,
include_inherent_vowel: true, // For Hindi/Bengali style typing
};
let mut ctx = TypingContext::new(Script::Devanagari, Some(options));
let diff = ctx.take_key_input("k");
println!("{}", diff.diff_add_text); // क (with inherent vowel)
}

After creating a typing context, you can dynamically update typing options:

  • ctx.update_use_native_numerals(bool) - Enable/disable native numerals (e.g., १० instead of 10)
  • ctx.update_include_inherent_vowel(bool) - Enable/disable inherent vowel inclusion (schwa deletion)
  • ctx.get_use_native_numerals() - Get current native numerals setting
  • ctx.get_include_inherent_vowel() - Get current inherent vowel setting
  • ctx.get_normalized_script() - Get current normalized script name
  • TypingContext — Stateful context for typing mode

    • new(typing_script: Script, options: Option<TypingContextOptions>) — Create new context
    • take_key_input(&mut self, key: &str) — Process single character input
    • clear_context(&mut self) — Clear internal state
  • TypingContextOptions — Configuration for typing behavior

    • auto_context_clear_time_ms: u64 — Idle timeout for auto-clear (only when the std feature is enabled; default: 4500ms)
    • use_native_numerals: bool — Use script-native numerals (default: true)
    • include_inherent_vowel: bool — Include inherent vowel/schwa (default: false)
  • TypingDiff — Result of processing a key input

    • to_delete_chars_count: usize — Characters to delete from current state
    • diff_add_text: String — Text to insert
  • ScriptTypingDataMap — Typing data for a script

    • common_krama_map: Vec<TypingDataMapItem> — Common character mappings
    • script_specific_krama_map: Vec<TypingDataMapItem> — Script-specific mappings
  • ListType — Character type enum: Anya, Vyanjana, Matra, Svara

  • TypingDataMapItem — Type alias for (String, ListType, Vec<String>)

  • Embedded Data: All script data is embedded at compile time using rust-embed, eliminating runtime file I/O
  • Static Lifetime: Script data is cached with 'static lifetime for zero-cost access
  • Zero Allocations: The transliteration core minimizes allocations where possible
  • Type Safety: Rust’s type system prevents common runtime errors

Most APIs accept Script enum values directly and do not return Result. The only place where error handling is needed is when parsing a string into a Script:

use std::str::FromStr;
use lipilekhika::Script;
match Script::from_str("InvalidScript") {
Ok(script) => println!("Parsed: {:?}", script),
Err(e) => eprintln!("Unknown script: {}", e),
}

Once you have a valid Script, all other functions (transliterate, get_all_options, TypingContext::new, etc.) are infallible.