npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

skir-rust-gen

v0.1.2

Published

[![npm](https://img.shields.io/npm/v/skir-rust-gen)](https://www.npmjs.com/package/skir-rust-gen) [![build](https://github.com/gepheum/skir-rust-gen/workflows/Build/badge.svg)](https://github.com/gepheum/skir-rust-gen/actions)

Readme

npm build

Skir's Rust code generator

Official plugin for generating Rust code from .skir files.

Targets Rust edition 2021 and higher.

Set up

In your skir.yml file, add the following snippet under generators:

  - mod: skir-rust-gen
    outDir: ./src/skirout
    config: {}

The generated Rust code has a runtime dependency on the skir-client crate. Add it to your Cargo.toml with:

skir-client = "0.1"

In your src/lib.rs (or src/main.rs), expose the generated module:

pub mod skirout;

Then create src/skirout.rs with the following content (the generator will populate the base subdirectory):

// The skirout module is generated by `npx skir gen`. Do not edit by hand.
pub mod base;

For more information, see this Rust project example.

Rust generated code guide

The examples below are for the code generated from this .skir file.

Referring to generated symbols

// Import generated types from the Rust module generated from "user.skir".
use crate::skirout::base::user::{
    tarzan_const, SubscriptionStatus, SubscriptionStatus_Trial, User, UserRegistry, User_Pet,
};

// Now you can use: User, SubscriptionStatus, tarzan_const(), etc.

Struct types

Skir generates a plain Rust struct for every struct in the .skir file. All fields are public. Every struct implements Default, Clone, PartialEq, and Debug.

// Construct a User by filling in each field directly.
let john = User {
    user_id: 42,
    name: "John Doe".to_string(),
    quote: "Coffee is just a socially acceptable form of rage.".to_string(),
    pets: vec![User_Pet {
        name: "Dumbo".to_string(),
        height_in_meters: 1.0,
        picture: "🐘".to_string(),
        _unrecognized: None,
    }],
    subscription_status: SubscriptionStatus::Free,
    _unrecognized: None, // Present in every struct; always set to None
};

println!("{}", john.name); // John Doe

// User::default() returns a User with every field set to its default value.
println!("{}", User::default().name);    // (empty string)
println!("{}", User::default().user_id); // 0

// Struct update syntax: specify some fields and use defaults for the rest.
let jane = User {
    user_id: 43,
    name: "Jane Doe".to_string(),
    ..User::default()
};

println!("{}", jane.quote);      // (empty string)
println!("{}", jane.pets.len()); // 0

Creating modified copies

// Option 1: clone, then mutate.
let mut evil_john = john.clone();
evil_john.name = "Evil John".to_string();
evil_john.quote = "I solemnly swear I am up to no good.".to_string();

println!("{}", evil_john.name);    // Evil John
println!("{}", evil_john.user_id); // 42 (copied from john)
println!("{}", john.name);         // John Doe (john is unchanged)

// Option 2: use struct update syntax to copy fields from an existing instance.
let evil_john_2 = User {
    name: "Evil John".to_string(),
    quote: "I solemnly swear I am up to no good.".to_string(),
    ..john.clone()
};

println!("{}", evil_john_2.name);    // Evil John
println!("{}", evil_john_2.user_id); // 42 (copied from john)
println!("{}", john.name);           // John Doe (john is unchanged)

Enum types

Skir generates a Rust enum for every enum in the .skir file. The Unknown variant is added automatically and is the default.

The definition of the SubscriptionStatus enum in the .skir file is:

enum SubscriptionStatus {
  FREE;
  trial: Trial;
  PREMIUM;
}

Making enum values

let _ = [
    // Unknown is the default and is present in all Skir enums.
    SubscriptionStatus::Unknown(None),
    SubscriptionStatus::default(), // Same as ::Unknown(None)
    SubscriptionStatus::Free,
    SubscriptionStatus::Premium,
    // Wrapper variants carry a value inside a Box.
    SubscriptionStatus::Trial(Box::new(SubscriptionStatus_Trial {
        start_time: std::time::SystemTime::now(),
        _unrecognized: None,
    })),
];

Conditions on enums

// Direct match on enum variants:
let get_info_text = |status: &SubscriptionStatus| -> String {
    match status {
        SubscriptionStatus::Free => "Free user".to_string(),
        SubscriptionStatus::Premium => "Premium user".to_string(),
        SubscriptionStatus::Trial(t) => {
            format!("On trial since {:?}", t.start_time)
        }
        SubscriptionStatus::Unknown(_) => "Unknown subscription status".to_string(),
    }
};

println!("{}", get_info_text(&john.subscription_status)); // Free user

Serialization

User::serializer() returns a Serializer<User> which can serialise and deserialise instances of User.

use skir_client::{JsonFlavor, Serializer, UnrecognizedValues};

let serializer = User::serializer();

// Serialize to dense JSON (field-number-based; the default mode).
// Use this when you plan to deserialize the value later. Because field
// names are not included, renaming a field remains backward-compatible.
let john_dense_json = serializer.to_json(&john, JsonFlavor::Dense);
println!("{}", john_dense_json);
// [42,"John Doe",...]

// Serialize to readable (name-based, indented) JSON.
// Use this mainly for debugging.
println!("{}", serializer.to_json(&john, JsonFlavor::Readable));
// {
//   "user_id": 42,
//   "name": "John Doe",
//   "quote": "Coffee is just a socially acceptable form of rage.",
//   "pets": [
//     {
//       "name": "Dumbo",
//       "height_in_meters": 1.0,
//       "picture": "🐘"
//     }
//   ],
//   "subscription_status": "FREE"
// }

// The dense JSON flavor is the flavor you should pick if you intend to
// deserialize the value in the future. Skir allows fields to be renamed, and
// because fields names are not part of the dense JSON, renaming a field does
// not prevent you from deserializing the value.
// You should pick the readable flavor mostly for debugging purposes.

// Deserialize from JSON (both dense and readable formats are accepted).
let reserialized_john = serializer
    .from_json(&john_dense_json, UnrecognizedValues::Drop)
    .unwrap();
assert_eq!(reserialized_john, john);

// Serialize to binary format (more compact than JSON; useful when
// performance matters, though the difference is rarely significant).
let john_bytes = serializer.to_bytes(&john);
let from_bytes = serializer
    .from_bytes(&john_bytes, UnrecognizedValues::Drop)
    .unwrap();
assert_eq!(from_bytes, john);

Primitive serializers

println!("{}", Serializer::bool().to_json(&true, JsonFlavor::Dense));
// 1
println!("{}", Serializer::int32().to_json(&3_i32, JsonFlavor::Dense));
// 3
println!(
    "{}",
    Serializer::int64().to_json(&9_223_372_036_854_775_807_i64, JsonFlavor::Dense)
);
// "9223372036854775807"
println!("{}", Serializer::float32().to_json(&1.5_f32, JsonFlavor::Dense));
// 1.5
println!("{}", Serializer::float64().to_json(&1.5_f64, JsonFlavor::Dense));
// 1.5
println!(
    "{}",
    Serializer::string().to_json(&"Foo".to_string(), JsonFlavor::Dense)
);
// "Foo"

Composite serializers

// Optional serializer:
println!(
    "{}",
    Serializer::optional(Serializer::string())
        .to_json(&Some("foo".to_string()), JsonFlavor::Dense)
);
// "foo"

println!(
    "{}",
    Serializer::optional(Serializer::string()).to_json(&None::<String>, JsonFlavor::Dense)
);
// null

// Array serializer:
println!(
    "{}",
    Serializer::array(Serializer::bool()).to_json(&vec![true, false], JsonFlavor::Dense)
);
// [1,0]

Constants

// Constants declared with 'const' in the .skir file are available as
// functions in the generated Rust code.
println!(
    "{}",
    User::serializer().to_json(tarzan_const(), JsonFlavor::Readable)
);
// {
//   "user_id": 123,
//   "name": "Tarzan",
//   "quote": "AAAAaAaAaAyAAAAaAaAaAyAAAAaAaAaA",
//   "pets": [
//     {
//       "name": "Cheeta",
//       "height_in_meters": 1.67,
//       "picture": "🐒"
//     }
//   ],
//   "subscription_status": {
//     "kind": "trial",
//     "value": {
//       "start_time": {
//         "unix_millis": 1743592409000,
//         "formatted": "2025-04-02T11:13:29.000Z"
//       }
//     }
//   }
// }

Keyed lists

// In the .skir file:
//   struct UserRegistry {
//     users: [User|user_id];
//   }
// The '|user_id' part tells Skir to generate a keyed array with O(1)
// lookup by user_id.

let user_registry = UserRegistry {
    users: vec![john.clone(), jane.clone(), evil_john.clone()].into(),
    _unrecognized: None,
};

// find_by_key returns the first element whose user_id matches.
// The first call is O(n) to build the index; subsequent calls are O(1).
let found = user_registry.users.find_by_key(43);
println!("{}", found.is_some());         // true
println!("{}", found.unwrap() == &jane); // true

// If multiple elements share the same key, the first one wins.
let found2 = user_registry.users.find_by_key(42);
println!("{}", found2.unwrap() == &john); // true

let not_found = user_registry.users.find_by_key(999_i32);
println!("{}", not_found.is_none()); // true

// find_by_key_or_default() returns a reference to the default (zero) value
// when the key is not present, instead of returning None.
let not_found_or_default = user_registry.users.find_by_key_or_default(999_i32);
println!("{}", not_found_or_default.pets.len()); // 0

Skir services

Starting a Skir service on an HTTP server

Full example here.

Sending RPCs to a Skir service

Full example here.

Reflection

Reflection allows you to inspect a Skir type at runtime.

use skir_client::TypeDescriptor;

let td = User::serializer().type_descriptor();
if let TypeDescriptor::Struct(sd) = td {
    let names: Vec<&str> = sd.fields().iter().map(|f| f.name()).collect();
    println!("{:?}", names);
    // ["user_id", "name", "quote", "pets", "subscription_status"]
}

// A TypeDescriptor can be serialized to JSON and deserialized later.
let td2 = TypeDescriptor::parse_from_json(
    &User::serializer().type_descriptor().as_json(),
)
.unwrap();
if let TypeDescriptor::Struct(sd2) = td2 {
    println!("{}", sd2.fields().len()); // 5
}