Rewrite Gurmukhi Utils to Rust - #223
Merged
Merged
Conversation
Collaborator
|
Wow |
Implement to_ascii() converting Unicode Gurmukhi to ASCII for Open Gurbani Akhar font, mirroring the JS/Python implementations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mark as private with name "root" to avoid confusion with the published package, remove unused tsx dependency and broken test script. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate all language bindings under a single bindings/ directory. Update path references in CODEOWNERS, CONTRIBUTING.md, README.md, constants generators, test fixture paths, and guut adapters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add mise tasks for building Rust and generating UniFFI bindings for Ruby, Kotlin, and Swift. Update .gitignore to exclude generated binding files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete pure Ruby reimplementation (ascii.rb, unicode.rb, remove.rb, constants.rb) and replace with UniFFI-generated FFI bindings from the Rust core. Update gemspec to depend on ffi gem, rewrite tests to use the new API surface (to_ascii, to_unicode, normalize_unicode). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Kotlin package with build.gradle.kts (JNA + Kotlin JVM), guut test adapter for the universal JSON test suite, and mise.toml for Java 21 toolchain. Bindings are generated from Rust via uniffi-bindgen at build time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Swift package with Package.swift (system library target for the C FFI header + modulemap), XCTest-based guut adapter for the universal JSON test suite. Bindings are generated from Rust via uniffi-bindgen at build time, split into Sources/GurmukhiUtils/ (.swift) and Sources/gurmukhi_utilsFFI/ (.h + .modulemap). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete empty csharp/ directory (no implementation was started). Update progress matrix to show Rust, Kotlin, and Swift columns. Remove C# maintainer reference and contributing link. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generalizes vishram/line-ending operations into a composable Markup enum. Callers pass arrays of markup types to remove() or detect() instead of calling separate per-type functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generalize the markup module into a feature module that covers all removable/detectable categories of Gurmukhi text: vishrams, granular line endings (rahao/numbered/bare), vowel signs, vowel carriers, nukta, adhak, nasals, accents, and visarga. Add uniffi-exported grouping functions (vishraams, line_endings, vowels, modifiers, all_features) and feature_chars for character lookup — available in all binding languages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Needed by the transcribe module to normalize input before dispatching to scheme-specific conversion logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…schemes Single `transcribe(input, script)` API for converting Gurmukhi into other scripts. Each Script enum variant bundles opinionated conversion behavior for its target audience: - LatinScholar: mechanical ISO/IAST-like transliteration for academics - Latin: pronunciation-aware conversion for English readers in sangat (haha rules, grammatical vowel dropping, adhak gemination) - Devanagari: offset-based script mapping for Hindi readers 440 test cases ported from Python + new Devanagari cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ports the 2 remaining guru_latn_pa assertion cases that were initially skipped. The Python has a bug where variation selectors leak into the output for these inputs — the Rust implementation handles them correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rust core (via UniFFI) is now the single source of truth. Removes all native language implementations that duplicated Rust functionality: - JavaScript: src/, test/, tsconfig, prettierignore - Python: gurmukhiutils/, tests/, poetry.lock, .flake8 - Dart: entire binding (no viable UniFFI generator exists) Moves language-specific gitignore entries from root to each binding folder (UniFFI generated paths for Kotlin/Ruby/Swift, standard patterns for JS/Python). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The constants/ directory generated language-specific constant files for the native JS/Python/Ruby implementations. Now that all bindings use Rust via UniFFI, constants are embedded in the Rust library and this pipeline is orphaned. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gen-node-js Replace ubrn (uniffi-bindgen-react-native) with two purpose-built generators: - uniffi-bindgen-node-js: native FFI for Node/Bun (loads .so via koffi) - uniffi-bindgen-js: WASM for browser (self-contained, 2 files + .wasm) Both produce matching APIs: camelCase functions, string-valued enums, clean TypeScript types. Post-processing scripts handle camelCase-ification of native output and namespace flattening of WASM output. Adds mise tasks: generate:javascript, generate:javascript:native, generate:javascript:wasm, build:wasm. Removes ubrn dependency and patch-enums.ts workaround. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up `mise run generate:python` using the standard uniffi-bindgen CLI with `--language python`. Generates ctypes-based bindings into bindings/python/gurmukhiutils/. Thin __init__.py re-exports everything for clean `from gurmukhiutils import to_ascii, Script` usage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rust tests are the source of truth. Per-language guut tests that validated native implementations against universal JSON test data are redundant now that all bindings are generated from UniFFI. Removes: test/ JSON suite, guut runners (Ruby/Kotlin/Swift), per-language test files, Rakefile/Gemfile, test tasks from mise.toml, test deps from build.gradle.kts and Package.swift. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove per-binding CONTRIBUTING.md and README.md files that described old native development workflows (poetry, guut, npm test, spec/). Fix Python pyproject.toml URLs to point to monorepo. Remove deprecated Python .vscode settings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run cargo fmt, fix $crate in macro, redundant closure, empty doc comment, and remove unused is_below_letter function. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add uniffi-bindgen-js and uniffi-bindgen-node-js as [[bin]] targets pinned via Cargo.lock for version-correct codegen, matching the existing uniffi-bindgen.rs pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each test imports the binding and calls toUnicode("gurU", SantLipi),
asserting the result equals "ਗੁਰੂ". Validates the full chain:
Rust → FFI → binding → runtime → correct output.
- JS WASM, Python, Ruby, Kotlin, Swift smoke tests
- Mise smoke:* tasks for local execution
- Fix Swift Package.swift target names after crate rename
- Strip use "Darwin" from generated modulemap at generation time
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the generate matrix job to smoke test each language after generation. Adds conditional setup for Kotlin (JDK) and Swift (compiler) since those aren't in the base mise toolset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds cargo-zigbuild CI matrix to compile libgurmukhi for 5 platforms (linux x64/arm64, macOS x64/arm64, Windows x64) from a single Linux runner. Extends publish workflow with Python wheels, Ruby gems, and Kotlin fat JAR packaging. - Reusable build-native.yml workflow called from CI and publish - Python: setuptools with platform-tagged wheels per target - Ruby: renamed gem to gurmukhi, patched ffi_lib to load from gem dir - Kotlin: maven-publish plugin, JNA resource dirs for fat JAR - Local: mise run build:native / build:native:all tasks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- smoke:stage only creates symlinks if binding dir exists (CI runs per-language, so other bindings' dirs aren't present) - sudo gem install for CI Ruby ffi gem - rename job from "Generate + Smoke" to "Generate" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix Ruby symlink: 4 levels up from bindings/ruby/lib/gurmukhi/ - Switch Kotlin smoke to Gradle runSmoke task (resolves JNA dep) - Add setup-gradle action for Kotlin CI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ffi_lib with an absolute path doesn't append extensions. Use
FFI.map_library_name('gurmukhi') to get the correct platform name
(libgurmukhi.so, libgurmukhi.dylib, gurmukhi.dll).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generate bindings once on Linux, then smoke test each language on all 3 platforms. Validates native library loading actually works on each OS, not just the CI build platform. - Generate job uploads bindings as artifacts - Smoke job: language × os matrix downloads bindings + native lib - Exclude swift on windows (no Swift runtime) - Remove legacy gurmukhi_utils wrapper files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ruby FFI gem needs sudo on Linux CI runners - macOS runners have Swift built-in; setup-swift causes linker conflicts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rror libswiftCompatibility56.a causes arm64 fixup errors on modern macOS linkers when SPM doesn't know the deployment target. Setting macOS 13 / iOS 16 avoids the compatibility library entirely. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-compiled dylibs have absolute Linux paths baked as install names. Use install_name_tool to fix to @rpath, and pass -rpath to swift build so the runtime linker finds the library. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SPM resolves packages from the repo root, not subdirectories. Move Package.swift to root and update paths to reference bindings/swift/Sources. Update smoke:swift task and gitignore accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add tsc compilation to generate:javascript so the npm package ships compiled JS with type declarations instead of raw .ts — works in Node, Bun, Deno, and all bundlers without extra tooling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generate (javascript) failed in CI with TS2688 because @types/node was never installed before tsc ran. Add bun install to the task and migrate to bun.lock, matching the repo's bun toolchain. Ship a size-optimized WASM: dedicated wasm-release profile (opt-level=z, fat LTO, panic=abort, strip) plus wasm-opt -Oz after bindgen, taking the binary from 1.63 MB to 950 KB raw (335 KB gzip). Native library builds keep the speed-optimized release profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012giEdJSNKHM8sEuahhmonn
binaryen's version_XXX tag scheme makes ubi's "latest" resolve to a stale 2019 semver tag (1.39.1) on newer mise, which ships no wasm-opt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012giEdJSNKHM8sEuahhmonn
… notes JS bindings export enums as string-literal union types, not runtime objects — the examples showed imports that don't exist. Also document ESM-only/top-level-await requirements and bundler support for the WASM asset pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012giEdJSNKHM8sEuahhmonn
…package One package, one import: Metro resolves the react-native export condition to a JSI Turbo Module backed by the compiled Rust library; every other bundler falls through to the wasm entry. - ubrn 0.31.0-3 (matches uniffi 0.31) generates the JSI bindings, C++ glue, Android/iOS scaffolding, and Codegen spec - Hand-maintained src/index.rn.ts adapter presents the same string-literal enum surface as the wasm bindings so both entry points are interchangeable, typechecked via tsconfig.rn.json - Smoke test runs the generated bindings under Node through ubrn's N-API runtime (@ubjs/node) — no emulator needed - CI: Generate (react-native) job + Android (cargo-ndk, 3 ABIs) and iOS (xcframework) cross-compile jobs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012giEdJSNKHM8sEuahhmonn
publish.yml gains a JavaScript pipeline: wasm + JSI generation, Android jniLibs and iOS xcframework cross-compiles, assembled into one npm package (dry-run always; real publish on release with NPM_TOKEN). release-please now bumps every binding's version via extra-files (package.json, pyproject.toml, gemspec, build.gradle.kts) so second and subsequent releases don't collide on any registry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012giEdJSNKHM8sEuahhmonn
harjot1singh
marked this pull request as ready for review
July 18, 2026 19:00
harjot1singh
requested review from
akshdeep-singh,
bhajneet,
dsomel21 and
onkarjit
as code owners
July 18, 2026 19:00
harjot1singh
enabled auto-merge (rebase)
July 18, 2026 19:00
harjot1singh
disabled auto-merge
July 18, 2026 19:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wouldn't it be great if we had a singular codebase, and could generate the same gurmukhi-utils for each language?
Well, now we can! This PR rewrites gurmukhi-utils to Rust. Bindings are generated by Uniffi for multiple language targets: Python, Ruby, Kotlin, Swift, and JavaScript.
The JavaScript story is a single
gurmukhinpm package serving every runtime through conditional exports:react-nativeexport condition). Android jniLibs (arm64-v8a, armeabi-v7a, x86_64) and an iOS xcframework are cross-compiled in CIBoth entry points present an identical API surface (string-literal enums), enforced by a typechecked adapter.
Why?
Pros
Drawbacks
See MIGRATING.md for the old→new API mapping per language.
Hopefully, a win 🤞🏼