Native Modules (Author Guide)
How to create third-party native modules (e.g. tish-polars, tish-egui) that extend Tish with Rust.
This document specifies the formal requirements for third-party native modules that extend Tish with Rust-based capabilities.
Overview
Third-party modules integrate with Tish via:
- Interpreter:
TishNativeModuletrait; globals registered at startup - Compiled: Import-driven;
import { Egui } from 'tish:egui'resolves the package and adds it as a Cargo dependency. Notish_runtimeglue. - Package: npm
package.jsonwithtish.module,tish.crate,tish.export; resolved fromnode_modulesor workspace layout
1. Trait Contract
TishNativeModule (Interpreter)
All native modules must implement TishNativeModule from tish_eval:
pub trait TishNativeModule: Send + Sync {
fn name(&self) -> &'static str;
fn register(&self) -> HashMap<Arc<str>, Value>;
}name()— Module identifier (e.g."Polars").register()— ReturnsHashMap<global_name, Value>of globals to inject.- Must be
Send + Syncfor thread safety.
Opaque Types
For values that wrap Rust types (e.g. DataFrames):
- Implement
tish_core::TishOpaquefor method dispatch. - Expose via
Value::Opaque(Arc::new(your_type)). - Methods are invoked via
get_method; returnValueor callNativeFncallbacks.
Native Functions
- Use
Value::Native(fn_ptr)orEvalValue::Native(fn_ptr)for callbacks. - Native functions receive
Valuearguments and returnValueorResult<Value, String>.
2. Version Compatibility
- Minimum tish version: Document in module's README or
peerDependencies. - Rust edition:
2021. - Cargo resolver:
"2"when using workspace. - MSRV: Match Tish's minimum supported Rust version.
3. Dependency Contract
Required Crates (Interpreter Path)
tish_core— Core types (Value,TishOpaque,NativeFn).tish_eval—TishNativeModule,Evaluator,Value.tish_parser— If parsing Tish source (optional).
Feature Alignment
Third-party tish_eval features must match what the module uses:
http— Forfetch, timers,serve.fs— ForreadFile,writeFile, etc.process— Forprocess.env, etc.regex— ForRegExp, etc.
Compiled Path
For compiled output support:
- Implement an export function (e.g.
egui_object()) that returnstish_core::Value(a module object built withtish_core::tish_module!or manually). - Add
package.jsonwithtish.module: true,tish.crate,tish.export(the Rust function name). - Tish resolves
tish:eguietc. via package lookup, generatesCargo.tomlwith the module crate as a dependency, and emits calls liketish_egui::egui_object(). - No changes to
tish_runtimeortish_compile— modules are fully external.
4. Package Layout (Standalone)
Cargo Layout
- Package name:
tish-<domain>(e.g.tish-polars). - Feature naming: Match tish's feature name (e.g.
polars). - Path assumption: Standalone modules may use
path = "../tish/crates/..."; document layout in README.
npm Package Layout
tish-polars/
├── package.json # name, version, tish.module, tish.feature, tish.crate
├── Cargo.toml
├── src/
│ └── lib.rs
package.json (tish module):
{
"name": "tish-polars",
"version": "0.1.0",
"tish": {
"module": true,
"crate": "tish-polars",
"export": "polars_object"
}
}tish.module: true— Identifies this package as a tish native module.tish.crate— Cargo crate name (used for the generated Cargo dependency).tish.export— Rust function name that returns the moduleValue(e.g.polars_object). Default:{module}_object(e.g.egui→egui_object).
5. Security and Stability
- Transitive deps: Avoid pulling heavy or unstable dependencies without justification.
- Pinning: Recommend caret or exact pins for tish crates in third-party
Cargo.toml. - No dynamic loading: Extensions are statically linked; no
.soplugins at runtime.
6. Registry and Resolution
- npm: Tish native modules are published to npm as regular packages.
- Convention:
tish-*prefix ortish.module: trueidentifies native modules. - Resolution: Use
dependenciesinpackage.json;npm installpopulatesnode_modules. tish tooling mapstish-*deps to Cargo features.