Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Jim Tcl in Rust

This is the documentation site for the rust-jim project, which supplies two things:

  • The jimtcl crate, and its underlying jimtcl-sys, to enable Jim Tcl to be embedded in Rust programs.
  • Guardian Tcl, an extended version of Jim Tcl with additional commands leveraging the rich Rust ecosystem.

The rust-jim repository on Codeberg has the source code and issue tracker. Pull requests welcome!

Embedding Jim Tcl

The jimtcl crate is the primary entry point to embed stock Jim Tcl into Rust. It uses the separate jimtcl-sys crate that provides the Jim Tcl source code and low-level FFI bindings.

The API reference is the primary documentation for using this crate.

Jim is compiled and statically linked into the executable. Linking against a system-provided Jim is TBD.

Note

Currently, Jim Tcl only supports Unix-like systems. Windows suppport is planned but not yet implemented.

Crate Features

The following features control Rust integration, and are enabled by default:

  • cli — add a dependency on Clap and provide a reusable command line for Jim Tcl shells. Enables the rust-jimsh binary as well.
  • serde — add a DeserializeSeed implementation to allow Jim objects to be directly deserialized with Serde format libraries. Serialization is not yet supported, as it would require some kind of schema mechanism.

Extension Features

Each of the Jim Tcl optional extensions, except for database extensions, is exposed through a Rust crate feature, and passed through to a correpsonding feature on jimtcl-sys. They are all enabled by default.

  • aio
  • array
  • binary
  • clock
  • ensemble
  • eventloop
  • exec
  • file
  • glob
  • history
  • interp
  • json (enables both json and jsonencode extensions)
  • namespace
  • oo
  • pack
  • package
  • posix
  • readdir
  • regexp
  • signal
  • tclcompat
  • tree

Introducing Guartcl

Guardian is Jimmy Olsen with superhero gear.

Guartcl is Jim Tcl with extra bits.

Guardian Tcl is provided by the guartcl crate, and supplies a guarsh binary to run Guardian Tcl scripts.

Guardian uses Jim with all default extensions enabled, except for SSL. For full details on the Jim Tcl commands, see Jim Tcl manual. These pages only document Guardian’s extensions to Jim Tcl.

Installing

Guardian Tcl release binaries can be downloaded from Codeberg. The easiest way to install them is with cargo-binstall:

$ cargo binstall guartcl

You can also install with UBI:

$ ubi --forge=forgejo --project mdekstrand/rust-jim -r guartcl -e guarsh

You can also install Guardian with Mise. Add the following to your mise.toml or mise/config.toml:

[tools."forgejo:mdekstrand/rust-jim"]
version = "latest"
matching = "guartcl"

Commands

Guardian Tcl currenly adds one ensemble command, parse. This command exposes subcommands to parse data from a variety of formats directly into nested Tcl data structures.

parse

The parse command has the following usage:

parse <format> <data>
parse <format> -file <path>
  • <format> can be any of json, toml, or yaml.
  • <data> is a Tcl string containing the raw data to deserialize.
  • <path> is the path to a file containing data in the specified format.

For example, to deserialize the contents of the $conf_src variable as JSON, you can write:

set config [parse json $conf_src]

These commands are wrappers around the corresponding Serde deserializers. You can also parse by parsing to typedata and using td native to obtain a native Tcl structure.

uuid

The uuid ensemble can generate and inspect UUIDs (backed by the uuid crate). UUIDs are represented as hexadecimal strings without braces.

uuid v3 namespace name
Generate a version 3 (MD5) UUID with the specified namespace and name. namespace can be either a UUID, or one of the strings oid, url, dns, or x500.
uuid v4
Generate a version 4 (random) UUID.
uuid v5 namespace name
Generate a version 5 (SHA1) UUID. Parameters are identical to uuid v3.
uuid v7
Generate a version 7 (timestamp + random, sortable) UUID.
uuid version uuid
Query the version number of the specified UUID.
uuid timestamp uuid
Query the timestamp of the specified UUID (v1, v6, or v7).
uuid random
Generate a random UUID (currently alias for uuid v4).
uuid bytes
Obtain the binary representation of the UUID as a 16-byte binary string.

Binary Extensions

Guardian Tcl extends the binary ensemble with binary encode and binary decode commands. These function much like the standard Tcl binary encode/decode commands1, except they do not support any options.

binary encode encoding data
Encode data with encoding.
binary decode encoding data
Decode data with encoding.

It also provides a command to generate random bytes:

binary random n
Generate n random bytes as a binary string. This uses the system’s (secure) random number generator via getrandom.

Supported Encoding

The following encodings are supported:


  1. https://www.tcl-lang.org/man/tcl9.1/TclCmd/binary.html#M5 ↩

Type Data (td)

Note

These commands are only available in Guardian Tcl 0.5 or later, which is currently in prerelease.

Guardian Tcl provides a “typed data” structure, accessed through the td ensemble command, that represents JSON-like data along with type information, instead of Tcl’s usual string-based representation. It is similar in that respect to Huddle, but uses JSON as its string representation and checks types in Rust. You can think of it as a native-code Huddle, however. Any valid JSON string is a valid TypedData object.

Note

JSON is the string representation as of 0.5, but this may change in the future if we find a need for data types not supported by JSON.

Creating Typed Data

The following commands create a typed data value:

td null
Create a null value.
td boolean val
Create a boolean value. val is any valid Tcl boolean.
td integer n
Create an integer value with value n.
td float x
Create a floating-point value with value x.
td string str
Create a string value with value str.
td list value…
Create a list value with the value(s) provided as arguments. The list elements themselves must already be typed data.
td record key value …
Create a record (dictionary) value with the key-value pairs provided as arguments (works like dict create). The values themselves must already be typed data.

Conversion

td native td
Convert the typed data td to Tcl-native layout (values, lists, and dicts).
td coalesce td …
Return the first non-null argument.

Conversion to native is not directly reversible, because Tcl-native format loses the type information from the original. Since typed data is stored as JSON, use the Jim Tcl json::encode command to convert Tcl-native data to JSON.

Querying Typed Data

These commands all query typed data, and require the typed data itself as input.

td get ?-native? td key…
Traverses the typed data td by the specified keys path to query an record. The first key is looked up in td, the second in the first key’s value, and so forth. Keys can be strings for records, or valid indices for lists. The full Tcl list index syntax (e.g. end, end-1) is supported.
td type td
Get the type of the typed data value.
td length td
Get the length of the typed data value if it is a record, list, or string. Fails for other types of data.

Manipulating Typed Data

Unlike the other typed-data functions, these commands take a tdvar parameter specifying the name of the variable storing the typed data to manipulate.

Important

Typed data values are effectively immutable, and manipulating typed data updates the variable to store a new typed data. In particular, using these functions on the result of a td get command will not modify the original data structure that td get queried.

td set tdvar key… value
Update the location in the record specified by the key path to store value. If a key does not exist in a record value, it will be added (this does not work for lists).
td lappend tdvar key… ?–? value…
Append the specified values to the list addressed by the key path. Fails if the key path does not specify a list. Separating the key path from the values with -- is required if you want to add more than one value to the list.

Data Format Interchange

Parsing

td parse json ?-file? input
Parse the specified JSON input as typed data. Since typed data is represented as JSON, this just validates the input. The -file option instructs it to treat input as a filename instead of JSON text.
td parse yaml ?-file? input
Just like td parse json, except it parses YAML.
td parse toml ?-file? input
Just like td parse json, except it parses TOML.

Dumping

Typed data can also be dumped to several formats.

td dump json ?-pretty? ?-file outfile? td
Save the typed data as JSON, optionally to a file, optionally with pretty-printing. The file is saved with a terminating newline in either case.
td dump toml ?-pretty? ?-file outfile? td
Save the typed data as TOML, optionally to a file, optionally with pretty-printing.
td dump yaml ?-file outfile? td
Save the typed data as TOML, optionally to a file.

Date-Time Support

Since 0.7, Guardian Tcl includes an experimental dt command providing a modern date-time manipulation API that is aware of calendars, time zones, and similar complexifiers. It is a powered by (and designed around) the Rust Jiff library, which in turn is heavily inspired by JavaScript Temporal.

Warning

The Guardian dt command is highly experimental and may change significantly as I figure out what an ergonomic, modern Tcl date/time API looks like.

Date Object Types

The dt commands operate on multiple types of date/time objects (referred to below as dto). The supported types map to different Jiff objects, and are as follows:

zoned
An instant in time with an associated time zone. This is the most complete date/time type, and corresponds to jiff::Zoned. The time zone may be a fixed offset, or it may be a transition-aware time zone such as America/New_York.
datetime
A date and time without an associated time zone. It is ambiguous whether such times are local or UTC, and interpretation is determined by context and/or command options.
date
A date without a time. Represented in ISO format (e.g., 2026-10-09).
time
A time without a date. Represented as HH:MM:SS[.SS…]
timestamp
An instant in time, represented as seconds since the epoch UTC or an ISO Zulu date-time. Integer and floating-point numbers are interpreted as timestamps when passed to dt commands.

The dt commands interpret strings as the most precise object they can successfully parse into. When serialized as strings, they are normalized to the corresponding ISO format as implemented by Jiff.

Note

There is not currently any mechanism to work with local timestamps (seconds since the epoch local time).

Current Time

dt now
Obtain the current time as a zoned.

Query / Extraction

dt type dto
Query the type of date object stored in dto.
dt date dto
Extract the date part of dto.
dt time dto
Extract the time part of dto.
dt timestamp dto
Convert dto to a Unix timestamp (seconds since the epoch).
dt year dto
Extract the year from dto.
dt month dto
Extract the month from dto.
dt day dto
Extract the day from dto.
dt week dto
Extract the ISO week-date week number from dto. ISO weeks start on Monday. See [Weeks][#weeks] for further discussion.
dt weekdate dto
Convert the date in dto to an ISO week date (e.g., 2026-03-29 becomes 2026-W13-7). See Weeks for further discussion.
dt weekday ?-named? dto
Extract the weekday of dto. This is an ISO weekday number (see Weeks), or a lowercase weekday name if -named is passed.
dt is complete dto
Query whether dto is a complete specification of an exact point in time. Timestamps and zoned date-times are complete;, dates, times, and unzoned date-time objects are not complete.
dt is span obj
Query whether obj is a span.
dt same month dto1 dto2
Query whether dto1 and dto2 occur in the same month (and year).
dt same week dto1 dto2
Query whether dto1 and dto2 occur in same year and ISO (Monday-based) week.

Display and Formatting

dt format fmt dto
Format dto according to fmt. The format specifier fmt uses the syntax of jiff::fmt::strtime, which is mostly compatible with Unix strftime. Truly locale-aware formatting is not yet supported.
dt rfc2822 ?-local? dto
Display the date/time dto in RFC 2822 (HTTP / e-mail header) format. With ?-local?, converts to local time first. To display the current local time in RFC 2822, you can write dt rfc2822 -local now.
dt friendly dto
Display a “friendly” version of dto (or a span), intended for human consumption. This format has no stability guarantees, although the friendly version of a span is the same as Jiff’s, and will round-trip for commands like dt add.

Parsing / Normalization

dt parse ?flags? str

Parse a date-time, ensuring it is valid and possibly converting it. The supported flags are:

-local
Parse into local time, yielding a zoned. Zoned and timestamp inputs are translated to the system’s local time, and unzoned date-time inputs are interpreted as local time and turned into zoned. Bare dates and times are unchanged.
-utc
Like -local, but interprets or translates to UTC instead of the system’s local time.
-span
Parse a span (such as 1mo or 2h50m) instead of a date/time. This validates that the object is properly interpretable as a span by Jiff, and pre-parses it into a jiff::Span for subsequent use by other commands like dt add.
-format fmt
Parse a date according to fmt, which is a jiff::fmt::strtime format string.

Manipulation

dt add dto span

Add the specified span to dto. The base date can be any supported date/time object, although not all date and span combinations are possible (e.g., adding a month to a time will fail).

dt adjust dto how args

Adjust the specified dto to a different day. how specifies the kind adjustment, and args provides additional information. The following combinations of how and args are supported:

next weekday, prev weekday
Change to the next or previous weekday (mon, tue, etc.)
[+-]N weekday
Advance (or step back) by N weekdays, e.g. +2 mondays
first weekday, last weekday
Adjust to the first or last weekday of the date’s month. second, third, etc. (up to fifth) are also accepted.
to zone zone
Adjust to time zone zone. If the dto has a time zone, it is adjusted to be in the specified zone. If it does not, then it is interpreted in the specified zone without adjustment.
to local, to utc
Adjust to local or UTC time zone. Unzoned dates are interpreted to be in the taret zone.

Examples:

# adjust to 2026-10-12
dt adjust 2026-10-10 next monday
# find US Thanksgiving - Nov. 26
dt adjust 2026-11-01 fourth thursday
dt subtract dto rhs

Subtract rhs (either a span or a DTO) from a DTO, yielding a DTO or a span.

String Representations

The following string representations are supported for calendar date/time objects:

  • ISO timestamp (YYYY-MM-DDTHH:MM:SS.SSSZ), parsed as a timestamp. Seconds and fractional seconds are optional.
  • Unix timestamps (either integer or float), parsed as a timestamp.
  • ISO dates (YYYY-MM-DD), parsed as a date.
  • ISO week dates (YYYY-Wnn-D), parsed as a date.
  • Times (HH:MM:SS.SSS), parsed as a time. Seconds and fractional seconds are optional.
  • ISO date-time (YYYY-MM-DDTHH:MM[:SS[.SSS]]), parsed as an unzoned datetime. A space may be used instead of T. Seconds and fractional seconds are optional.
  • ISO date-time with offset (YYYY-MM-DDTHH:MM:SS.SSS+OO:oo), parsed as a zoned with a fixed-offset time zone. A space may be used instead of T. Seconds and fractional seconds are optional.
  • ISO date-time with zone (YYYY-MM-DDTHH:MM:SS.SSS[Zone]), parsed as a zoned. Zone can be any time zone name or abbreviation supported by the system. A space may be used instead of T. Seconds and fractional seconds are optional.

Spans are represented in ISO duration format, e.g. P1M or P2DT3H. Note that the duration elements are interpreted in calendar-space: P1D and P24H are not the same when added to a date/time with a DST adjustment or a leap second, because the former moves a zoned date-time by one day regardless of how many hours are in the day, and the latter moves it by 24 hours.

Human-readable duration notation, such as 2h 30m, is also accepted as input where a span is required.

Weeks

For consistency, the dt command suite operates on ISO weeks consistently for week-related operations. Weeks start on Monday, and weekdays are numbered from 1, so Sunday is weekday 7 (although 0 may be accepted in some places).

ISO weeks are numbered from the first week of the year, starting with 1, and a week’s year is defined as the Gregorian year in which its Thursday occurs. A consequence of this is that some dates at the beginning or end of a normal Gregorian calendar year may appear in different ISO years. For example:

  • If Jan. 1 is on a Friday, then it appears in the previous year in ISO week-date format. Friday, Jan. 1, 2027 becomes 2026-W53-5.
  • If Jan. 1 is on a Wednesday, then late December will appear in the next year. Wednesday, Dec. 31, 2025 becomes 2026-W01-3.

The week-related commands (dt week, dt weekdate, dt weekday, and dt same week) all operate on ISO weeks. Gregorian dates are converted to ISO weeks when needed. dt same week therefore checks whether two dates are in the same ISO week (and year), which is usually what you want for answering “do these two dates appear in the same week?”, unless you need weeks to begin on Sunday. I may consider future extensions to provide options for Sunday-based weeks, but this was the easy way to get code with good probability of correctness based on Jiff’s capabilities.

HTTP Client

The Guardian http command provides an HTTP(S) client to Tcl scripts.

It is currently based on ureq, but this is not guaranteed.

Important

This command is currently in early preview, it may change in upcoming releases. It is not enabled by default, but must be explicitly enabled with the http feature.

Note

The http command currently requires the URL to be untainted, and taints the response data and response headers. Query parameters and request headers are not currently required to be untainted, but this is not guaranteed and feedback is welcome on what should be checked for tainted data.

Tip

The platform-ssl Rust feature, enabled by default, uses the host platform’s SSL certificates and validation logic HTTPS certificates. If this feature is disabled, a built-in certificate bundle is used.

HTTP GET

The http get command exposes GET requests:

http get ?-query dict? ?-headers dict? ?-headers-out var? ?-out-file file? ?-binary? url

-query dict

Add query parameters to the request from the dictionary dict.

-headers dict

Add HTTP request headers from the dictionary dict.

-headers-out var

Save response headers to a dictionary in variable var.

-out-file file

Save output to file instead of returning it. Saves the data directly, ignoring -binary.

-binary

Return binary output instead of decoding to string.

Note

This just uses read_to_vec() instead of read_to_string(). When it is not supplied, it is not clear if ureq decodes according to the content-encoding, or just treats data as UTF-8.

Compiling Binaries

Guardian Tcl supports building stand-alone executables containing both the Tcl interpreter and a Tcl script. This functionality is exposed through the guardian::compile Tcl command. It is intended to function somewhat like Tclkits for OG Tcl, but is more limited and based on Jim Tcl with the Guardian extensions.

guardian::compile ?-usage? ?-out exe? script ?packages?
Compile script (the path to a Tcl file, not a Tcl script string) into a standalone executable. If -out is not specified, the output name is derived from the script name. -usage enables CLI parsing based on Usage specifications embedded in the script. Additional Tcl source files named as packages are included in the compiled executable and can be accessed with package require.