
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
skir-rust-gen
Advanced tools
[](https://www.npmjs.com/package/skir-rust-gen) [](https://github.com/gepheum/skir-rust-gen/actions)
Official plugin for generating Rust code from .skir files.
Targets Rust edition 2021 and higher.
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.
The examples below are for the code generated from this .skir file.
// 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.
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
// 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)
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;
}
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,
})),
];
// 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
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);
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"
// 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 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"
// }
// }
// }
// }
// 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
Full example here.
Full example here.
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
[](https://www.npmjs.com/package/skir-rust-gen) [](https://github.com/gepheum/skir-rust-gen/actions)
We found that skir-rust-gen demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.