### Initialize Skir Project Source: https://skir.build/docs/setup Initializes a new Skir project by creating the `skir.yml` configuration file and an example `.skir` file. This is the first step in setting up a new project. ```bash npx skir init ``` -------------------------------- ### Runtime Reflection in Kotlin Source: https://skir.build/docs/kotlin Demonstrates how to use reflection in Kotlin to inspect Skir types at runtime. It shows how to get field names and how to serialize/deserialize type descriptors. ```kotlin println( User.typeDescriptor .fields .map { field -> field.name } .toList(), ) // [user_id, name, quote, pets, subscription_status] // A type descriptor can be serialized to JSON and deserialized later. val typeDescriptor = TypeDescriptor.parseFromJsonCode( User.serializer.typeDescriptor.asJsonCode(), ) assert(typeDescriptor is StructDescriptor) assert((typeDescriptor as StructDescriptor).fields.size == 5) ``` -------------------------------- ### Install Skir Client Runtime Dependency Source: https://skir.build/docs/typescript Command to install the `skir-client` npm package, which is a runtime dependency for Skir-generated TypeScript code. This package provides necessary utilities for the generated code to function correctly. ```shell npm i skir-client ``` -------------------------------- ### GitHub Actions Workflow for Skir Codegen Source: https://skir.build/docs/setup Example of a GitHub Actions workflow step to run Skir code generation. It assumes Node.js and `npx` are pre-installed on the runner. Additional steps for formatting checks and snapshot verification are also shown. ```yaml - name: Run Skir codegen run: npx skir gen ``` ```yaml - name: Run Skir format checker run: npx skir format --ci ``` ```yaml - name: Ensure Skir snapshot up-to-date run: npx skir snapshot --ci ``` -------------------------------- ### Import Generated Dart Symbols Source: https://skir.build/docs/dart Example of importing symbols generated by Skir from a Dart module. Assumes the module is named 'user.skir' and the output is in './skirout'. ```dart // Import the given symbols from the Dart module generated from "user.skir" import 'package:skir_dart_example/skirout/user.dart'; // Now you can use: tarzan, User, UserHistory, UserRegistry, etc. ``` -------------------------------- ### Unit Testing with Mocha and Buckwheat in TypeScript Source: https://skir.build/docs/typescript Provides an example of writing unit tests using Mocha and Buckwheat frameworks. It demonstrates assertions on object properties, including regular expressions for string matching and range checks for numerical values. ```typescript expect(tarzan).toMatch({ name: "Tarzan", quote: /^A/, // must start with the letter A pets: [ { name: "Cheeta", heightInMeters: near(1.6, 0.1), }, ], subscriptionStatus: { union: { kind: "trial", value: { startTime: Timestamp.fromUnixMillis(1234), }, }, }, // `userId` is not specified so it can be anything }); ``` -------------------------------- ### Protobuf Map Example with Redundancy Source: https://skir.build/docs/protobuf A Protobuf example illustrating the 'map' type, where the key ('id') is redundant as it's also stored within the 'User' message value, leading to potential inconsistencies. ```protobuf message User { string id = 1; string name = 2; } message UserRegistry { // Redundant: 'id' is stored in the map key AND the User map users = 1; } ``` -------------------------------- ### Protobuf Enum Example Source: https://skir.build/docs/protobuf An example of a Protobuf enum definition, demonstrating the need for a separate 'oneof' for stateful options and the manual inclusion of an UNKNOWN value. ```protobuf // poker.proto message PokerAction { enum Enum { UNKNOWN = 0; CHECK = 1; BET = 2; CALL = 3; FOLD = 4; RAISE = 5; } Enum action = 1; // Only if action is BET or RAISE int32 amount = 2; } ``` -------------------------------- ### Accessing Constants in Dart Source: https://skir.build/docs/dart Shows how to print and inspect predefined constants in Dart. This example displays the structure and values of a `User` constant, including nested objects and enums. ```dart print(tarzan); // User( // userId: 123, // name: "Tarzan", // quote: "AAAAaAaAaAyAAAAaAaAaAyAAAAaAaAaA", // pets: [ // User_Pet( // name: "Cheeta", // heightInMeters: 1.67, // picture: "🐒", // ), // ], // subscriptionStatus: SubscriptionStatus.wrapTrial( // SubscriptionStatus_Trial( // startTime: DateTime.fromMillisecondsSinceEpoch( // // 2025-04-02T11:13:29.000Z // 1743592409000 // ), // ) // ), // ) ``` -------------------------------- ### Unit Testing Structs with GoogleTest Struct Matchers (C++) Source: https://skir.build/docs/cpp Illustrates how to write unit tests for C++ structs using GoogleTest's `StructIs` matcher. This allows for targeted testing of specific struct fields, including nested structs and floating-point comparisons. The example tests a `User` struct and its nested `Pet` struct. ```cpp const User john = { .name = "John Doe", .pets = { {.height_in_meters = 1.67, .name = "Cheeta", .picture = "🐒"}, }, .quote = "Life is like a box of chocolates.", .user_id = 42, }; EXPECT_THAT(john, (StructIs{ // Only the specified fields are tested .pets = testing::ElementsAre(StructIs{ .height_in_meters = testing::FloatNear(1.7, 0.1), }), .quote = testing::StartsWith("Life is"), .user_id = 42, })); ``` -------------------------------- ### Use primitive and composite serializers in Java Source: https://skir.build/docs/java Provides examples of using built-in serializers for primitive types and composite structures like Optionals and Lists to convert data to JSON strings. ```java assert Serializers.bool().toJsonCode(true).equals("1"); assert Serializers.int32().toJsonCode(3).equals("3"); assert Serializers.string().toJsonCode("Foo").equals("\"Foo\""); assert Serializers.javaOptional(Serializers.string()) .toJsonCode(java.util.Optional.of("foo")) .equals("\"foo\""); assert Serializers.list(Serializers.bool()) .toJsonCode(List.of(true, false)) .equals("[1,0]"); ``` -------------------------------- ### GET /api?studio Source: https://skir.build/docs/services Access the built-in RPC Studio interface for interactive API exploration, testing, and documentation. ```APIDOC ## GET /api?studio ### Description Serves the RPC Studio web interface, which allows developers to inspect available service methods, view schemas, and execute test requests directly from the browser. ### Method GET ### Endpoint /api?studio ### Parameters #### Query Parameters - **studio** (flag) - Required - Triggers the rendering of the interactive documentation UI. ``` -------------------------------- ### Working with Frozen Structs Source: https://skir.build/docs/python Demonstrates how to instantiate frozen (immutable) structs using constructors or partial static factory methods, and how they enforce deep immutability. ```python john = User( user_id=42, name="John Doe", quote="Coffee is just a socially acceptable form of rage.", pets=[ User.Pet( name="Dumbo", height_in_meters=1.0, picture="🐘", ), ], subscription_status=SubscriptionStatus.FREE ) assert john.name == "John Doe" assert isinstance(john.pets, tuple) jane = User.partial( user_id=43, name="Jane Doe", ) assert jane.quote == "" assert User.DEFAULT == User.partial() ``` -------------------------------- ### Format Skir Files Source: https://skir.build/docs/setup Formats all `.skir` files in the project to ensure consistent style and readability. This command helps maintain code quality. ```bash npx skir format ``` -------------------------------- ### Accessing Constants in Kotlin Source: https://skir.build/docs/kotlin Shows how to print a constant value named TARZAN in Kotlin. The example includes a commented-out representation of a User object for context. ```kotlin println(TARZAN) // User( // userId = 123, // name = "Tarzan", // quote = "AAAAaAaAaAyAAAAaAaAaAyAAAAaAaAaA", // pets = listOf( // User.Pet( // name = "Cheeta", // heightInMeters = 1.67F, // picture = "🐒", // ), // ), // subscriptionStatus = SubscriptionStatus.TrialWrapper( // SubscriptionStatus.Trial( // startTime = Instant.ofEpochMillis( // // 2025-04-02T11:13:29Z // 1743592409000L // ), // ) // ), // ) ``` -------------------------------- ### Importing Generated Java Symbols Source: https://skir.build/docs/java Example of importing symbols generated by Skir from a Java module. This includes types for user data, registries, status, and constants. ```java // Import the given symbols from the Java module generated from "user.skir" import skirout.user.User; import skirout.user.UserRegistry; import skirout.user.SubscriptionStatus; import skirout.user.Constants; // Now you can use: Constants.TARZAN, User, UserRegistry, SubscriptionStatus, etc. ``` -------------------------------- ### Accessing Skir Constants Source: https://skir.build/docs/cpp Demonstrates how to access predefined constants within the Skir library. The example shows retrieving a constant `User` object named `tarzan` and asserting its properties. ```cpp const User& tarzan = skirout_user::k_tarzan(); assert(tarzan.name == "Tarzan"); ``` -------------------------------- ### Configure Skir Generator Source: https://skir.build/docs/python Configuration snippet for the skir.yml file to enable Python code generation and the shell command to install the necessary runtime dependency. ```yaml - mod: skir-python-gen outDir: ./src/skirout config: {} ``` ```shell pip install skir-client ``` -------------------------------- ### Keyed Lists in Kotlin Source: https://skir.build/docs/kotlin Illustrates the usage of keyed lists in Kotlin, specifically for a UserRegistry. It demonstrates finding users by their key (user_id) and the performance characteristics of these lookups. ```kotlin // In the .skir file: // struct UserRegistry { // users: [User|user_id]; // } val userRegistry = UserRegistry(users = listOf(john, jane, evilJohn)) // find() returns the user with the given key (specified in the .skir file). // In this example, the key is the user id. // The first lookup runs in O(N) time, and the following lookups run in O(1) // time. assert(userRegistry.users.findByKey(43) === jane) // If multiple elements have the same key, the last one is returned. assert(userRegistry.users.findByKey(42) === evilJohn) assert(userRegistry.users.findByKey(100) == null) ``` -------------------------------- ### Utilize Primitive and Composite Serializers in Dart Source: https://skir.build/docs/dart Provides examples of using Skir's built-in primitive serializers (bool, int, string, etc.) and composite serializers (optional, iterable) for custom data handling. ```dart assert(skir.Serializers.bool.toJson(true) == 1); assert(skir.Serializers.int32.toJson(3) == 3); assert(skir.Serializers.optional(skir.Serializers.string).toJson("foo") == "foo"); print(skir.Serializers.iterable(skir.Serializers.bool).toJson([true, false])); ``` -------------------------------- ### Serialize and deserialize objects in Java Source: https://skir.build/docs/java Explains how to use the static SERIALIZER property to convert objects to dense/readable JSON or binary formats, and how to reconstruct objects from these formats. ```java final Serializer serializer = User.SERIALIZER; final String johnDenseJson = serializer.toJsonCode(john); final String johnReadableJson = serializer.toJsonCode(john, JsonFlavor.READABLE); final ByteString johnBytes = serializer.toBytes(john); final User reserializedJohn = serializer.fromJsonCode(johnDenseJson); final User reserializedFromBytes = serializer.fromBytes(johnBytes); ``` -------------------------------- ### Handling Enums and Pattern Matching Source: https://skir.build/docs/python Demonstrates creating enum variants, including those with wrapped values, and checking enum kinds using the union property. ```python roni_status = SubscriptionStatus.wrap_trial( SubscriptionStatus.Trial( start_time=skir.Timestamp.from_unix_millis(1744974198000), ) ) assert roni_status == SubscriptionStatus.create_trial( start_time=skir.Timestamp.from_unix_millis(1744974198000) ) assert roni_status.union.kind == "trial" ``` -------------------------------- ### Checking Java Enum Values and Variants Source: https://skir.build/docs/java Provides examples of checking the values of generated Java enums and accessing data within enum variants. Demonstrates using `equals()` for direct comparison and `as{VariantName}()` for accessing variant data. ```java assert john.subscriptionStatus().equals(SubscriptionStatus.FREE); // UNKNOWN is the default value for enums. assert jane.subscriptionStatus().equals(SubscriptionStatus.UNKNOWN); final Instant now = Instant.now(); final SubscriptionStatus trialStatus = SubscriptionStatus.wrapTrial( SubscriptionStatus.Trial.builder() .setStartTime(now) .build()); assert trialStatus.kind() == SubscriptionStatus.Kind.TRIAL_WRAPPER; assert trialStatus.asTrial().startTime() == now; // SubscriptionStatus.FREE.asTrial(); // ^ Runtime error: asTrial() can only be called on a trial wrapper. ``` -------------------------------- ### Add Skir Client Dependency Source: https://skir.build/docs/dart Adds the `skir_client` library as a runtime dependency in the `pubspec.yaml` file for the generated Dart code. ```yaml skir_client: any ``` -------------------------------- ### Add Skir C++ Client Dependency with CMake Source: https://skir.build/docs/cpp Integrate the Skir C++ client library into your CMake build system. This example uses FetchContent to declare and make available the skir-client from its GitHub repository. ```cmake include(FetchContent) FetchContent_Declare( skir-client GIT_REPOSITORY https://github.com/gepheum/skir-cc-gen.git GIT_TAG main # Or pick a specific commit/tag SOURCE_SUBDIR client ) FetchContent_MakeAvailable(skir-client) ``` -------------------------------- ### Utilize Primitive Serializers Source: https://skir.build/docs/kotlin Demonstrates how to use built-in primitive serializers for standard data types like booleans, integers, and timestamps. ```kotlin assert(Serializers.bool.toJsonCode(true) == "1") assert(Serializers.int32.toJsonCode(3) == "3") assert(Serializers.timestamp.toJsonCode(Instant.ofEpochMilli(1743682787000)) == "1743682787000") assert(Serializers.string.toJsonCode("Foo") == "\"Foo\"") ``` -------------------------------- ### Performing Keyed Array Lookups Source: https://skir.build/docs/python Demonstrates efficient O(1) lookup capabilities within Skir collections using 'find' and 'find_or_default' methods. ```python user_registry = UserRegistry(users=[john, jane, lyla_mut]) assert user_registry.users.find(42) == john assert user_registry.users.find_or_default(100).name == "" ``` -------------------------------- ### Interact with Skir Enums and Variants Source: https://skir.build/docs/kotlin Explains how to instantiate Skir-generated enum constants and wrapper variants, including the use of the default UNKNOWN constant. ```kotlin val someStatuses = listOf( SubscriptionStatus.UNKNOWN, SubscriptionStatus.FREE, SubscriptionStatus.PREMIUM, SubscriptionStatus.TrialWrapper( SubscriptionStatus.Trial( startTime = Instant.now(), ), ), SubscriptionStatus.createTrial( startTime = Instant.now(), ), ) ``` -------------------------------- ### Deserialize Skir Values from JSON and Binary Source: https://skir.build/docs/cpp Explains how to deserialize skir values from both JSON and binary formats using the `Parse` function. It shows examples of deserializing from dense JSON strings and binary data into a `User` object. ```cpp // Use Parse to deserialize a skir value from JSON or binary format. absl::StatusOr reserialized_john = skir::Parse(john_dense_json); assert(reserialized_john.ok() && *reserialized_john == john); reserialized_john = skir::Parse(john_bytes.as_string()); assert(reserialized_john.ok() && *reserialized_john == john); ``` -------------------------------- ### Converting Between Frozen and Mutable Source: https://skir.build/docs/python Explains how to convert between frozen and mutable states using to_mutable(), to_frozen(), and the replace() method. ```python evil_jane_mut = jane.to_mutable() evil_jane_mut.name = "Evil Jane" evil_jane: User = evil_jane_mut.to_frozen() evil_jane = evil_jane.replace(name="Evil Jane") assert evil_jane.user_id == 43 ``` -------------------------------- ### Serializing and Deserializing Data in Python Source: https://skir.build/docs/python Explains how to use the static 'serializer' property to convert objects to dense or readable JSON formats and restore them from JSON strings. ```python serializer = User.serializer # Serialize to dense JSON john_dense_json = serializer.to_json(john) # Deserialize from JSON assert john == serializer.from_json(john_dense_json) # Serialize to readable string readable_json = serializer.to_json_code(john, readable=True) ``` -------------------------------- ### Accessing Skir Constants Source: https://skir.build/docs/java Shows how to access pre-defined constants within the Skir framework, which are typically represented as structured data objects. ```java System.out.println(Constants.TARZAN); ``` -------------------------------- ### Composite Serializers in Kotlin Source: https://skir.build/docs/kotlin Demonstrates how to use composite serializers for optional types and lists in Kotlin. It shows serialization of strings and booleans to JSON-like representations. ```kotlin assert(Serializers.optional(Serializers.string).toJsonCode("foo") == "\"foo\"") assert(Serializers.optional(Serializers.string).toJsonCode(null) == "null") assert(Serializers.list(Serializers.bool).toJsonCode(listOf(true, false)) == "[1,0]") ``` -------------------------------- ### Inspecting Types with Reflection Source: https://skir.build/docs/java Demonstrates runtime inspection of Skir types using TypeDescriptor. This allows for field enumeration and serialization of type metadata to JSON. ```java import build.skir.reflection.StructDescriptor; import build.skir.reflection.TypeDescriptor; System.out.println(User.TYPE_DESCRIPTOR.getFields().stream().map((field) -> field.getName()).toList()); final TypeDescriptor typeDescriptor = TypeDescriptor.Companion.parseFromJsonCode(User.SERIALIZER.typeDescriptor().asJsonCode()); assert typeDescriptor instanceof StructDescriptor; assert ((StructDescriptor) typeDescriptor).getFields().size() == 5; ``` -------------------------------- ### Serialize Primitive and Composite Types in TypeScript Source: https://skir.build/docs/typescript Shows how to use primitive and composite serializers to handle basic types and collections like arrays or optional values. ```typescript assert(primitiveSerializer("bool").toJson(true) === 1); assert(primitiveSerializer("int32").toJson(3) === 3); assert(primitiveSerializer("timestamp").toJson(Timestamp.fromUnixMillis(1743682787000)) === 1743682787000); assert(optionalSerializer(primitiveSerializer("string")).toJson("foo") === "foo"); console.log(arraySerializer(primitiveSerializer("bool")).toJson([true, false])); ``` -------------------------------- ### Integrate Skir Generation into `package.json` Source: https://skir.build/docs/setup Adds the `skir gen` command to the `package.json` scripts, specifically using the `prebuild` hook. This ensures that Skir code is automatically regenerated before the build process. ```json { "scripts": { "prebuild": "skir gen", "build": "tsc" } } ``` -------------------------------- ### Branch on enum variants in Java Source: https://skir.build/docs/java Demonstrates two approaches for handling enum variants: using a switch expression for concise branching and the visitor pattern for compile-time safety and exhaustive handling. ```java final Function getInfoText = status -> switch (status.kind()) { case FREE_CONST -> "Free user"; case PREMIUM_CONST -> "Premium user"; case TRIAL_WRAPPER -> "On trial since " + status.asTrial().startTime(); case UNKNOWN -> "Unknown subscription status"; default -> throw new AssertionError("Unreachable"); }; final SubscriptionStatus.Visitor infoTextVisitor = new SubscriptionStatus.Visitor<>() { @Override public String onFree() { return "Free user"; } @Override public String onPremium() { return "Premium user"; } @Override public String onTrial(SubscriptionStatus.Trial trial) { return "On trial since " + trial.startTime(); } @Override public String onUnknown() { return "Unknown subscription status"; } }; System.out.println(john.subscriptionStatus().accept(infoTextVisitor)); ``` -------------------------------- ### Working with Mutable Structs Source: https://skir.build/docs/python Shows how to use the .Mutable class for modifying struct fields and utilizing mutable properties to handle nested struct updates. ```python lyla_mut = User.Mutable() lyla_mut.user_id = 44 lyla_mut.name = "Lyla Doe" joly_history_mut = UserHistory.Mutable() joly_history_mut.user = User.Mutable(user_id=45) # Accessing mutable properties for nested updates joly_history_mut.mutable_user.quote = "I am Joly." lyla_mut.mutable_pets.append(User.Pet.partial(name="Cupcake")) lyla_mut.mutable_pets.append(User.Pet.Mutable(name="Simba")) ``` -------------------------------- ### Manage Type Aliases for Skir Data Structures Source: https://skir.build/docs/kotlin Demonstrates the use of type aliases for Skir-generated sealed classes that handle both frozen and mutable implementations. ```kotlin val greet: (User_OrMutable) -> Unit = { println("Hello, $it") } greet(jane) greet(lyla) ``` -------------------------------- ### Construct Frozen Dart Structs Source: https://skir.build/docs/dart Demonstrates constructing immutable (frozen) Dart classes generated by Skir. Shows required fields, immutability, and using a mutable builder pattern. ```dart // To construct a frozen User, call the User constructor. final john = User( // All fields are required. userId: 42, name: "John Doe", quote: "Coffee is just a socially acceptable form of rage.", pets: [ User_Pet( name: "Dumbo", heightInMeters: 1.0, picture: "🐘", ), ], subscriptionStatus: SubscriptionStatus.free, // foo: "bar", // ^ Does not compile: 'foo' is not a field of User ); assert(john.name == "John Doe"); // john.name = "John Smith"; // ^ Does not compile: all the properties are read-only // You can also construct a frozen User using the builder pattern with a // mutable instance as the builder. final User jane = (User.mutable() ..userId = 43 ..name = "Jane Doe" ..pets = [ User_Pet(name: "Fluffy", heightInMeters: 0.2, picture: "🐱"), User_Pet.mutable() ..name = "Fido" ..heightInMeters = 0.25 ..picture = "🐶" ..toFrozen(), ]) .toFrozen(); // Fields not explicitly set are initialized to their default values. assert(jane.quote == ""); // User.defaultInstance is an instance of User with all fields set to their // default values. assert(User.defaultInstance.name == ""); assert(User.defaultInstance.pets.isEmpty); ``` -------------------------------- ### Primitive and Composite Serializers Source: https://skir.build/docs/python Shows how to manually invoke primitive serializers for types like bool, int64, and timestamp, as well as composite serializers for arrays and optional values. ```python assert skir.primitive_serializer("bool").to_json(True) == 1 assert skir.array_serializer(skir.primitive_serializer("bool")).to_json((True, False)) == [1, 0] assert skir.optional_serializer(skir.primitive_serializer("string")).to_json(None) is None ``` -------------------------------- ### Running Skir RPC Studio Demo Source: https://skir.build/docs/services This command launches a local demo of Skir's RPC Studio. RPC Studio is an interactive documentation and testing tool that comes with every Skir service, allowing users to explore the API schema and execute requests. ```bash npx skir-studio-demo ``` -------------------------------- ### Constructing and Initializing C++ Structs from Skir Source: https://skir.build/docs/cpp Demonstrates how to create and initialize C++ structs generated by Skir. It shows both direct member assignment and designated initializers, including the `whole` initializer for compile-time completeness checks. ```cpp // You can construct a struct like this: User john; john.user_id = 42; john.name = "John Doe"; // Or you can use the designated initialized syntax: User jane = { // Keep fields in alphabetical order .name = "Jane Doe", .pets = {{ .name = "Fluffy", .picture = "cat", }, { .name = "Rex", .picture = "dog", }}, .subscription_status = skirout::kPremium, .user_id = 43, }; // ${Struct}::whole forces you to initialize all the fields of the struct. // You will get a compile-time error if you miss one. User lyla = User::whole{ .name = "Lyla Doe", .pets = { User::Pet::whole{ .height_in_meters = 0.05f, .name = "Tiny", .picture = "\ud83d\udc00", }, }, .quote = "This is Lyla's world, you just live in it", .subscription_status = skirout::kFree, .user_id = 44, }; ``` -------------------------------- ### Import Generated Skir Symbols in TypeScript Source: https://skir.build/docs/typescript Example of importing generated types and symbols from the Skir output directory into a TypeScript file. This allows for type-safe usage of the generated data structures. ```typescript import { TARZAN, SubscriptionStatus, User, UserHistory, UserRegistry } from "../skirout/user"; ``` -------------------------------- ### Serialize and Deserialize Objects in Dart Source: https://skir.build/docs/dart Explains the use of static serializer properties to convert objects into dense JSON, readable JSON, or binary formats. Includes examples for deserializing data back into object instances. ```dart final serializer = User.serializer; // Serialization final String johnDenseJson = serializer.toJsonCode(john); final String johnReadableJson = serializer.toJsonCode(john, readableFlavor: true); final Uint8List johnBytes = serializer.toBytes(john); // Deserialization final reserializedJohn = serializer.fromJsonCode(johnDenseJson); final reserializedFromBytes = serializer.fromBytes(johnBytes); ``` -------------------------------- ### Immutable Lists and Copying in Kotlin Source: https://skir.build/docs/kotlin Explains and demonstrates how Skir handles list immutability and copying in Kotlin. It shows scenarios where shallow copies are made for mutable lists and no copies are made for already immutable lists. ```kotlin // Since all Skir objects are deeply immutable, all lists contained in a // Skir object are also deeply immutable. // This section helps understand when lists are copied and when they are // not. val pets: MutableList = mutableListOf( Pet.partial(name = "Fluffy", picture = "🐶"), Pet.partial(name = "Fido", picture = "🐻"), ) val jade = User.partial( name = "Jade", pets = pets, // ^ 'pets' is mutable, so Skir makes an immutable shallow copy of it ) assert(pets == jade.pets) assert(pets !== jade.pets) val jack = User.partial( name = "Jack", pets = jade.pets, // ^ 'jade.pets' is already immutable, so Skir does not make a copy ) assert(jack.pets === jade.pets) ``` -------------------------------- ### Configure Skir for Kotlin Generation Source: https://skir.build/docs/kotlin Configure the `skir.yml` file to use the `skir-kotlin-gen` generator. Specify the output directory for the generated Kotlin code. This setup ensures Skir generates Kotlin files in the designated location. ```yaml - mod: skir-kotlin-gen outDir: ./src/main/kotlin/skirout config: {} # Alternatively: # outDir: ./src/main/kotlin/my/project/skirout # config: # packagePrefix: my.project. ``` -------------------------------- ### Instantiating Types with Protobuf vs Skir Source: https://skir.build/docs/protobuf Demonstrates the difference in constructor safety. Protobuf allows partial initialization that won't break when fields are added, while Skir enforces strict constructors that trigger compile-time errors if fields are missing. ```Python (Protobuf) # my_script_with_protobuf.py # Adding 'email' to User message doesn't break this code. user = User() user.id = 123 user.name = "Alice" ``` ```Python (Skir) # my_script_with_skir.py # Static type checkers will raise an error if 'email' is added to User in the # schema file and this code is not updated. user = User( id=123, name="Alice", ) ``` -------------------------------- ### Creating Java Enum Values with Variants Source: https://skir.build/docs/java Shows how to construct instances of Java enums generated by Skir, including handling wrapper variants. Demonstrates using `wrap{VariantName}` for constructing specific enum cases. ```java final List someStatuses = List.of( // The UNKNOWN constant is present in all skir enums even if it is not // declared in the .skir file. SubscriptionStatus.UNKNOWN, SubscriptionStatus.FREE, SubscriptionStatus.PREMIUM, // To construct wrapper variants, call the wrap{VariantName} static // methods. SubscriptionStatus.wrapTrial( SubscriptionStatus.Trial.builder() .setStartTime(Instant.now()) .build())); ``` -------------------------------- ### Run Skir Code Generation Source: https://skir.build/docs/setup Executes the Skir code generation process based on the configuration in `skir.yml`. This command transpiles `.skir` files into target languages and updates the `skirout` directories. The `--watch` flag enables automatic regeneration upon file changes. ```bash npx skir gen ``` ```bash npx skir gen --watch ``` -------------------------------- ### Equality and Hashing for Skir Structs and Enums Source: https://skir.build/docs/cpp Explains that Skir structs and enums support equality comparison and hashing, enabling their use in collections like `absl::flat_hash_set`. The example shows inserting duplicate elements into a set, which are automatically handled. ```cpp absl::flat_hash_set user_set; user_set.insert(john); user_set.insert(jane); user_set.insert(jane); user_set.insert(lyla); assert(user_set.size() == 3); ``` -------------------------------- ### Create Enum Values in TypeScript Source: https://skir.build/docs/typescript Examples of creating enum values in TypeScript using Skir-generated enums. It covers direct assignment for simple enum variants, using `create` for named variants, and handling wrapper variants with `create({kind: ..., value: ...})`. ```typescript const johnStatus = SubscriptionStatus.FREE; const janeStatus = SubscriptionStatus.PREMIUM; const lylaStatus = SubscriptionStatus.create("PREMIUM"); // ^ same as SubscriptionStatus.PREMIUM const jolyStatus = SubscriptionStatus.UNKNOWN; // Use create({kind: ..., value: ...}) for wrapper variants. const roniStatus = SubscriptionStatus.create({ kind: "trial", value: { startTime: Timestamp.fromUnixMillis(1234), }, }); ``` -------------------------------- ### Access Mutable Versions of Struct Properties in Kotlin Source: https://skir.build/docs/kotlin Explains how to use mutable accessors like `mutableUser` and `mutablePets` to get a mutable version of a struct's property. If the property is already mutable, it's returned directly. If it's frozen, a mutable shallow copy is created and returned. ```kotlin // The 'mutableUser' getter provides access to a mutable version of 'user'. // If 'user' is already mutable, it returns it directly. // If 'user' is frozen, it creates a mutable shallow copy, assigns it to // 'user', and returns it. // The user is currently 'lyla', which is mutable. assert(userHistory.mutableUser === lyla) // Now assign a frozen User to 'user'. userHistory.user = john // Since 'john' is frozen, mutableUser makes a mutable shallow copy of it. userHistory.mutableUser.name = "John the Second" assert(userHistory.user.name == "John the Second") assert(userHistory.user.userId == 42) // Similarly, 'mutablePets' provides access to a mutable version of 'pets'. // It returns the existing list if already mutable, or creates and returns a // mutable shallow copy. lyla.mutablePets.add( User.Pet( name = "Simba", heightInMeters = 0.4f, picture = "🦁", ), ) lyla.mutablePets.add(User.Pet.Mutable(name = "Cupcake")) // lyla.pets.add(User.Pet.Mutable(name = "Cupcake")); // ^ Does not compile: 'User.pets' is read-only ``` -------------------------------- ### Performing Keyed Lookups in Skir Source: https://skir.build/docs/java Illustrates the use of keyed lists for efficient data retrieval. Lookups by key are performed in O(N) time initially and O(1) subsequently, returning the last element if multiple matches exist. ```java final UserRegistry userRegistry = UserRegistry.builder().setUsers(List.of(john, jane, evilJohn)).build(); assert userRegistry.users().findByKey(43) == jane; assert userRegistry.users().findByKey(42) == evilJohn; assert userRegistry.users().findByKey(100) == null; ``` -------------------------------- ### Capitalize Strings in Skir Types using Static Reflection (C++) Source: https://skir.build/docs/cpp Demonstrates how to use static reflection to recursively capitalize all string values within a skir type. This is useful for data manipulation and ensuring consistent string formatting. The example uses a predefined skir type `skirout_user` and the `CapitalizeStrings` function. ```cpp User tarzan_copy = skirout_user::k_tarzan(); // CapitalizeStrings recursively capitalizes all the strings found within a // skir value. CapitalizeStrings(tarzan_copy); std::cout << tarzan_copy << "\n"; // { // .user_id: 123, // .name: "TARZAN", // .quote: "AAAAAAAAAAYAAAAAAAAAAYAAAAAAAAAA", // .pets: { // { // .name: "CHEETA", // .height_in_meters: 1.67, // .picture: "🐒", // }, // }, // .subscription_status: // ::skirout::wrap_trial_start_time(absl::FromUnixMillis(1743592409000 /* // 2025-04-02T11:13:29+00:00 */)), // } ``` -------------------------------- ### Constructing Immutable Java Structs with Builders Source: https://skir.build/docs/java Demonstrates how to create immutable Java objects from Skir structs using the builder pattern. Shows required fields, alphabetical order constraint, and the use of `partialBuilder` for optional fields. ```java // To construct a User, use the builder pattern. final User john = User.builder() // All fields are required. The compiler will error if you miss one or if // you don't specify them in alphabetical order. .setName("John Doe") .setPets( List.of( User.Pet.builder() .setHeightInMeters(1.0f) .setName("Dumbo") .setPicture("🐘") .build())) .setQuote("Coffee is just a socially acceptable form of rage.") .setSubscriptionStatus(SubscriptionStatus.FREE) .setUserId(42) .build(); assert john.name().equals("John Doe"); // john.pets().clear(); // ^ Runtime error: the list is deeply immutable. // With partialBuilder(), you are not required to specify all the fields, // and there is no constraint on the order. final User jane = User.partialBuilder().setUserId(43).setName("Jane Doe").build(); // Fields not explicitly set are initialized to their default values. assert jane.quote().equals(""); assert jane.pets().equals(List.of()); // User.DEFAULT is an instance of User with all fields set to their default // values. assert User.DEFAULT.name().equals(""); assert User.DEFAULT.userId() == 0; ``` -------------------------------- ### Runtime Type Inspection with Skir Reflection in Dart Source: https://skir.build/docs/dart Demonstrates how to use Skir's reflection capabilities in Dart to inspect type information at runtime. It shows how to retrieve field names from a type descriptor and how to serialize/deserialize type descriptors. ```dart import 'package:skir/skir.dart' as skir; final fieldNames = []; for (final field in User.serializer.typeDescriptor.fields) { fieldNames.add(field.name); } print(fieldNames); // [user_id, name, quote, pets, subscription_status] // A type descriptor can be serialized to JSON and deserialized later. final typeDescriptor = skir.TypeDescriptor.parseFromJson( User.serializer.typeDescriptor.asJson, ); print("Type descriptor deserialized successfully"); ``` -------------------------------- ### Skir Field Numbering (Starts from 0, Sequential) Source: https://skir.build/docs/protobuf Skir requires struct fields to be numbered sequentially starting from 0. This approach allows for more efficient serialization and deserialization, often leveraging simple array indexing. ```skir struct Example { field0: int32; field1: string; } ``` -------------------------------- ### Protobuf Field Numbering (Starts from 1, Sparse) Source: https://skir.build/docs/protobuf Protobuf fields must be numbered starting from 1 and can be sparse, allowing gaps. This often necessitates more complex serialization/deserialization mechanisms like hash maps or switch statements. ```protobuf message Example { int32 field1 = 1; string field3 = 3; } ``` -------------------------------- ### Manage Enum Values and Wrapper Types in Dart Source: https://skir.build/docs/dart Demonstrates how to instantiate standard enum values and complex wrapper types using wrapX() and createX() methods. These methods allow for structured data association with enum variants. ```dart final johnStatus = SubscriptionStatus.free; final janeStatus = SubscriptionStatus.premium; final jolyStatus = SubscriptionStatus.unknown; final roniStatus = SubscriptionStatus.wrapTrial( SubscriptionStatus_Trial( startTime: DateTime.fromMillisecondsSinceEpoch(1234, isUtc: true)), ); final ericStatus = SubscriptionStatus.createTrial( startTime: DateTime.fromMillisecondsSinceEpoch(5678, isUtc: true), ); ``` -------------------------------- ### Inspect Skir types at runtime using Reflection Source: https://skir.build/docs/python Demonstrates how to access a type descriptor from a Skir serializer and export it as JSON. It also shows how to verify the integrity of the descriptor by round-tripping it through serialization and deserialization. ```python field_names: list[str] = [] user_type_descriptor = User.serializer.type_descriptor # 'user_type_descriptor' has information about User and all the types it # depends on. print(user_type_descriptor.as_json_code()) # A TypeDescriptor can be serialized and deserialized. assert user_type_descriptor == skir.reflection.TypeDescriptor.from_json_code( user_type_descriptor.as_json_code() ) ``` -------------------------------- ### Deserialize Data Structures in TypeScript Source: https://skir.build/docs/typescript Illustrates the use of 'fromJson', 'fromJsonCode', and 'fromBytes' methods to reconstruct objects from various serialized formats. ```typescript const reserializedJohn = serializer.fromJsonCode(johnDenseJsonCode); const reserializedJane = serializer.fromJsonCode(serializer.toJsonCode(jane, "readable")); assert(serializer.fromJson(johnDenseJson).name === "John Doe"); assert(serializer.fromBytes(johnBytes.toBuffer()).name === "John Doe"); ``` -------------------------------- ### Dynamic Reflection with Skir TypeDescriptors Source: https://skir.build/docs/cpp Illustrates how to use Skir's dynamic reflection capabilities to obtain a `TypeDescriptor` for a given type, such as `User`. It also shows that `TypeDescriptor` objects can be serialized to and deserialized from JSON. ```cpp using ::skir::reflection::GetTypeDescriptor; using ::skir::reflection::TypeDescriptor; // A TypeDescriptor describes a skir type. It contains the definition of all // the structs and enums referenced from the type. const TypeDescriptor& user_descriptor = GetTypeDescriptor(); // TypeDescriptor can be serialized/deserialized to/from JSON. absl::StatusOr reserialized_type_descriptor = TypeDescriptor::FromJson(user_descriptor.AsJson()); assert(reserialized_type_descriptor.ok()); ``` -------------------------------- ### Configure Skir Code Generation (`skir.yml`) Source: https://skir.build/docs/setup Defines the code generators to be used and their configurations within the `skir.yml` file. It specifies the module (`mod`), output directory (`outDir`), and generator-specific settings (`config`). Paths are relative to the `skir.yml` file. ```yaml # skir.yml generators: - mod: skir-cc-gen outDir: ./app/src/skirout config: writeGoogleTestHeaders: true - mod: skir-typescript-gen outDir: ./frontend/skirout config: {} ``` -------------------------------- ### Define Skir schema versions Source: https://skir.build/docs/schema-evolution Example of schema evolution showing the addition of a new field and an enum variant between two versions of a User struct. ```text struct UserBefore(999) { id: int64; subscription_status: enum { FREE; PREMIUM; }; } struct UserAfter(999) { id: int64; subscription_status: enum { FREE; PREMIUM; TRIAL; // Added }; name: string; // Added } ``` -------------------------------- ### Constructing and Using C++ Enums from Skir Source: https://skir.build/docs/cpp Illustrates how to construct enum values generated by Skir, using both constant and wrapper variants. It covers accessing enum values and handling wrapper types with `is_` and `as_` methods. ```cpp // Use skirout::${kFieldName} or ${Enum}::${kFieldName} for constant variants. SubscriptionStatus john_status = skirout::kFree; SubscriptionStatus jane_status = skirout::kPremium; SubscriptionStatus lara_status = SubscriptionStatus::kFree; // Compilation error: MONDAY is not a field of the SubscriptionStatus enum. // SubscriptionStatus sara_status = skirout::kMonday; // Use wrap_${field_name} for wrapper variants. SubscriptionStatus jade_status = skirout::wrap_trial(SubscriptionStatus::Trial({ .start_time = absl::FromUnixMillis(1743682787000), })); SubscriptionStatus roni_status = SubscriptionStatus::wrap_trial({ .start_time = absl::FromUnixMillis(1743682787000), }); ``` -------------------------------- ### Wrap Method Inputs and Outputs with Structs in Skir Source: https://skir.build/docs/best-practices Illustrates wrapping method input and output parameters in structs to allow for future expansion without breaking changes. This provides a more extensible API design. ```Skir method IsPalindrome( struct { word: string; } ): struct { result: bool; } = 2000; ``` ```Skir method AnalyzeWord( struct { word: string; case_sensitive: bool; // New field } ): struct { is_palindrome: bool; is_semordnilap: bool; // New field } = 2000; ``` -------------------------------- ### Configure Skir Dart Generator Source: https://skir.build/docs/dart Configuration for the Skir Dart generator in the `skir.yml` file. Specifies the generator module and output directory for generated Dart code. ```yaml - mod: skir-dart-gen outDir: ./src/skirout config: {} ```