New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

skir-rust-gen

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

skir-rust-gen

[![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)

latest
Source
npmnpm
Version
0.1.3
Version published
Maintainers
1
Created
Source

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

SkirRPC services

Starting a SkirRPC service on an HTTP server

Full example here.

Sending RPCs to a SkirRPC 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
}

FAQs

Package last updated on 04 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts