From 2fe093255d4f9cc6f17fdb71d1c042e5c72586ee Mon Sep 17 00:00:00 2001 From: CI Date: Fri, 13 Jun 2025 18:59:07 +0200 Subject: [PATCH 1/7] feat: add data binding --- README.md | 143 ++++++--- build/build.sh | 5 +- build/premake5_code_generator.lua | 6 +- samples/db_generator.riv | Bin 0 -> 375 bytes samples/rewards.riv | Bin 0 -> 216976 bytes src/main.cpp | 412 ++++++++++++++++++++------ templates/json_template.mustache | 27 ++ templates/viewmodel_template.mustache | 63 ++++ 8 files changed, 517 insertions(+), 139 deletions(-) create mode 100644 samples/db_generator.riv create mode 100644 samples/rewards.riv create mode 100644 templates/viewmodel_template.mustache diff --git a/README.md b/README.md index ee5c18c..dcdae6f 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Run the code generator using: ``` Example: + ```sh ./build/out/lib/release/rive_code_generator -i ./examples/rive_files/animation.riv -o ./examples/generated_code.dart -l dart ``` @@ -81,6 +82,7 @@ Sample templates are available in the [`templates`](./templates) directory. The tool uses [Mustache](https://mustache.github.io/) templating. Please refer to the [Mustache documentation](https://mustache.github.io/) for syntax details. For each Rive file `{{#riv_files}}`, the following variables are available: + - `{{riv_pascal_case}}`: The Rive file name in PascalCase - `{{riv_camel_case}}`: The Rive file name in camelCase - `{{riv_snake_case}}`: The Rive file name in snake_case @@ -88,57 +90,108 @@ For each Rive file `{{#riv_files}}`, the following variables are available: - `{{assets}}`: List of assets in the Rive file - For each asset `{{#assets}}`: - - `{{asset_name}}`: Name of the asset - - `{{asset_camel_case}}`: Name of the asset in camelCase - - `{{asset_pascal_case}}`: Name of the asset in PascalCase - - `{{asset_snake_case}}`: Name of the asset in snake_case - - `{{asset_kebab_case}}`: Name of the asset in kebab-case - - `{{asset_type}}`: Type of the asset - - `{{asset_id}}`: ID of the asset - - `{{asset_cdn_uuid}}`: CDN UUID of the asset - - `{{asset_cdn_base_url}}`: CDN base URL of the asset + + - `{{asset_name}}`: Name of the asset + - `{{asset_camel_case}}`: Name of the asset in camelCase + - `{{asset_pascal_case}}`: Name of the asset in PascalCase + - `{{asset_snake_case}}`: Name of the asset in snake_case + - `{{asset_kebab_case}}`: Name of the asset in kebab-case + - `{{asset_type}}`: Type of the asset + - `{{asset_id}}`: ID of the asset + - `{{asset_cdn_uuid}}`: CDN UUID of the asset + - `{{asset_cdn_base_url}}`: CDN base URL of the asset - For each artboard `{{#artboards}}`: - - `{{artboard_name}}`: The original artboard name - - `{{artboard_pascal_case}}`: The artboard name in PascalCase - - `{{artboard_camel_case}}`: The artboard name in camelCase - - `{{artboard_snake_case}}`: The artboard name in snake_case - - `{{artboard_kebab_case}}`: The artboard name in kebab-case - - `{{animations}}`: List of animation names for the artboard - - For each animation `{{#animations}}`: - - `{{animation_name}}`: Name of the animation - - `{{animation_camel_case}}`: Name of the animation in camelCase - - `{{animation_pascal_case}}`: Name of the animation in PascalCase - - `{{animation_snake_case}}`: Name of the animation in snake_case - - `{{animation_kebab_case}}`: Name of the animation in kebab-case - - `{{state_machines}}`: List of state machines for the artboard - - For each state machine `{{#state_machines}}`: - - `{{state_machine_name}}`: Name of the state machine - - `{{state_machine_camel_case}}`: Name of the state machine in camelCase - - `{{state_machine_pascal_case}}`: Name of the state machine in PascalCase - - `{{state_machine_snake_case}}`: Name of the state machine in snake_case - - `{{state_machine_kebab_case}}`: Name of the state machine in kebab-case - - `{{inputs}}`: List of inputs for the state machine - - For each input `{{#inputs}}`: - - `{{input_name}}`: Name of the input - - `{{input_type}}`: Type of the input - - `{{input_default_value}}`: Default value of the input - - `{{text_value_runs}}`: List of text value runs for the artboard - - For each text value run `{{#text_value_runs}}`: - - `{{text_value_run_name}}`: Name of the text value run - - `{{text_value_run_camel_case}}`: Name of the text value run in camelCase - - `{{text_value_run_pascal_case}}`: Name of the text value run in PascalCase - - `{{text_value_run_snake_case}}`: Name of the text value run in snake_case - - `{{text_value_run_kebab_case}}`: Name of the text value run in kebab-case - - `{{text_value_run_default}}`: Default value of the text value run - - `{{text_value_run_default_sanitized}}`: Default value of the text value run with special characters encoded - - For each nested text value run `{{#nested_text_value_runs}}`: - - `{{nested_text_value_run_name}}`: Name of the nested text value run - - `{{nested_text_value_run_path}}`: Path of the nested text value run + + - `{{artboard_name}}`: The original artboard name + - `{{artboard_pascal_case}}`: The artboard name in PascalCase + - `{{artboard_camel_case}}`: The artboard name in camelCase + - `{{artboard_snake_case}}`: The artboard name in snake_case + - `{{artboard_kebab_case}}`: The artboard name in kebab-case + - `{{animations}}`: List of animation names for the artboard + - For each animation `{{#animations}}`: + - `{{animation_name}}`: Name of the animation + - `{{animation_camel_case}}`: Name of the animation in camelCase + - `{{animation_pascal_case}}`: Name of the animation in PascalCase + - `{{animation_snake_case}}`: Name of the animation in snake_case + - `{{animation_kebab_case}}`: Name of the animation in kebab-case + - `{{state_machines}}`: List of state machines for the artboard + - For each state machine `{{#state_machines}}`: + - `{{state_machine_name}}`: Name of the state machine + - `{{state_machine_camel_case}}`: Name of the state machine in camelCase + - `{{state_machine_pascal_case}}`: Name of the state machine in PascalCase + - `{{state_machine_snake_case}}`: Name of the state machine in snake_case + - `{{state_machine_kebab_case}}`: Name of the state machine in kebab-case + - `{{inputs}}`: List of inputs for the state machine + - For each input `{{#inputs}}`: + - `{{input_name}}`: Name of the input + - `{{input_type}}`: Type of the input + - `{{input_default_value}}`: Default value of the input + - `{{text_value_runs}}`: List of text value runs for the artboard + - For each text value run `{{#text_value_runs}}`: + - `{{text_value_run_name}}`: Name of the text value run + - `{{text_value_run_camel_case}}`: Name of the text value run in camelCase + - `{{text_value_run_pascal_case}}`: Name of the text value run in PascalCase + - `{{text_value_run_snake_case}}`: Name of the text value run in snake_case + - `{{text_value_run_kebab_case}}`: Name of the text value run in kebab-case + - `{{text_value_run_default}}`: Default value of the text value run + - `{{text_value_run_default_sanitized}}`: Default value of the text value run with special characters encoded + - For each nested text value run `{{#nested_text_value_runs}}`: + + - `{{nested_text_value_run_name}}`: Name of the nested text value run + - `{{nested_text_value_run_path}}`: Path of the nested text value run + + - `{{enums}}`: List of enums in the Rive file + - For each enum `{{#enums}}`: + + - `{{enum_name}}`: Name of the enum + - `{{enum_camel_case}}`: Name of the enum in camelCase + - `{{enum_pascal_case}}`: Name of the enum in PascalCase + - `{{enum_snake_case}}`: Name of the enum in snake_case + - `{{enum_kebab_case}}`: Name of the enum in kebab-case + - `{{enum_values}}`: List of enum values + - For each enum value `{{#enum_values}}`: + - `{{enum_value_key}}`: Key of the enum value + - `{{enum_value_camel_case}}`: Key of the enum value in camelCase + - `{{enum_value_pascal_case}}`: Key of the enum value in PascalCase + - `{{enum_value_snake_case}}`: Key of the enum value in snake_case + - `{{enum_value_kebab_case}}`: Key of the enum value in kebab-case + + - `{{view_models}}`: List of view models in the Rive file + - For each view model `{{#view_models}}`: + - `{{view_model_name}}`: Name of the view model + - `{{view_model_camel_case}}`: Name of the view model in camelCase + - `{{view_model_pascal_case}}`: Name of the view model in PascalCase + - `{{view_model_snake_case}}`: Name of the view model in snake_case + - `{{view_model_kebab_case}}`: Name of the view model in kebab-case + - `{{properties}}`: List of properties in the view model + - For each property `{{#properties}}`: + - `{{property_name}}`: Name of the property + - `{{property_camel_case}}`: Name of the property in camelCase + - `{{property_pascal_case}}`: Name of the property in PascalCase + - `{{property_snake_case}}`: Name of the property in snake_case + - `{{property_kebab_case}}`: Name of the property in kebab-case + - `{{property_type}}`: Type information for the property + - For property type `{{#property_type}}`: + - `{{is_view_model}}`: Whether the property is a view model + - `{{is_enum}}`: Whether the property is an enum + - `{{is_string}}`: Whether the property is a string + - `{{is_number}}`: Whether the property is a number + - `{{is_integer}}`: Whether the property is an integer + - `{{is_boolean}}`: Whether the property is a boolean + - `{{is_color}}`: Whether the property is a color + - `{{is_list}}`: Whether the property is a list + - `{{is_trigger}}`: Whether the property is a trigger + - `{{backing_name}}`: Backing name of the property + - `{{backing_camel_case}}`: Backing name in camelCase + - `{{backing_pascal_case}}`: Backing name in PascalCase + - `{{backing_snake_case}}`: Backing name in snake_case + - `{{backing_kebab_case}}`: Backing name in kebab-case **:warning: Warning:** For duplicated names (e.g., multiple artboards, animations, or assets with the same name), the original unique names will be preserved. However, the case-converted versions (such as camelCase, PascalCase, etc.) will have a unique identifier attached to avoid conflicts. For example: + - Original names: "MyArtboard", "MyArtboard" - Unique camelCase: "myArtboard", "myArtboardU1" diff --git a/build/build.sh b/build/build.sh index 9658692..cf41103 100755 --- a/build/build.sh +++ b/build/build.sh @@ -34,7 +34,7 @@ $PREMAKE --version export PREMAKE_PATH="$PWD/../rive-runtime/build":$PREMAKE_PATH OUT=out/lib/$CONFIG -$PREMAKE --scripts=../build --file=./premake5_code_generator.lua --config=$CONFIG --out=$OUT gmake2 +$PREMAKE --scripts=../build --file=./premake5_code_generator.lua --no-rive-decoders --config=$CONFIG --out=$OUT gmake2 for var in "$@"; do if [[ $var = "clean" ]]; then @@ -52,7 +52,8 @@ for var in "$@"; do # $OUT/rive_code_generator -i ../samples/signage_v03.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache # $OUT/rive_code_generator -i ../samples/rating.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache # $OUT/rive_code_generator -i ../samples/ -o out/generated/rive_generated.dart -t ../templates/dart_template.mustache - $OUT/rive_code_generator -i ../samples/ -o out/generated/rive.json -t ../templates/json_template.mustache + $OUT/rive_code_generator -i ../samples/ -o out/generated/rive_viewmodel.dart -t ../templates/viewmodel_template.mustache + # $OUT/rive_code_generator -i ../samples/ -o out/generated/rive.json -t ../templates/json_template.mustache # $OUT/rive_code_generator -i ../samples/nested_test.riv -o out/rive_generated.dart --help # $OUT/rive_code_generator -i ../samples/ -o out/rive_generated.dart fi diff --git a/build/premake5_code_generator.lua b/build/premake5_code_generator.lua index 956056e..5de47fd 100644 --- a/build/premake5_code_generator.lua +++ b/build/premake5_code_generator.lua @@ -3,6 +3,7 @@ rive = '../rive-runtime' -- dofile(path.join(path.getabsolute(rive) .. '/build', 'premake5.lua')) -- dofile(path.join(path.getabsolute(rive) .. '/build', 'rive_build_config.lua')) dofile(path.join(path.getabsolute(rive) .. '/premake5_v2.lua')) +dofile(path.join(path.getabsolute(rive) .. '/renderer/premake5_pls_renderer.lua')) project('rive_code_generator') do @@ -13,17 +14,14 @@ do includedirs({ '../include', rive .. '/include', - -- miniaudio, - -- yoga, '../external/', }) - -- links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' }) links({ 'rive' }) files({ '../src/**.cpp', - rive .. '/utils/**.cpp', + rive .. '/utils/no_op_factory.cpp', }) -- buildoptions({ '-Wall', '-fno-exceptions', '-fno-rtti' }) diff --git a/samples/db_generator.riv b/samples/db_generator.riv new file mode 100644 index 0000000000000000000000000000000000000000..c49f1412c48c31e12ff3165c445d7fcb17dbf496 GIT binary patch literal 375 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
U{$$|vp9I%NK~%*!zp;in`WGX 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 literal 0 HcmV?d00001 diff --git a/src/main.cpp b/src/main.cpp index 4198713..057cd8b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,10 @@ #include "rive/generated/animation/state_machine_bool_base.hpp" #include "rive/generated/animation/state_machine_number_base.hpp" #include "rive/generated/animation/state_machine_trigger_base.hpp" +#include "rive/viewmodel/runtime/viewmodel_runtime.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/data_enum_value.hpp" +#include "rive/viewmodel/viewmodel_property_enum.hpp" #include "utils/no_op_factory.hpp" #include #include @@ -37,23 +41,27 @@ enum class Language JavaScript }; -struct InputInfo { +struct InputInfo +{ std::string name; std::string type; std::string default_value; }; -struct TextValueRunInfo { +struct TextValueRunInfo +{ std::string name; std::string default_value; }; -struct NestedTextValueRunInfo { +struct NestedTextValueRunInfo +{ std::string name; std::string path; }; -struct AssetInfo { +struct AssetInfo +{ std::string name; std::string type; std::string file_extension; @@ -62,6 +70,30 @@ struct AssetInfo { std::string cdn_base_url; }; +struct EnumValueInfo +{ + std::string key; +}; + +struct EnumInfo +{ + std::string name; + std::vector values; +}; + +struct PropertyInfo +{ + std::string name; + std::string type; + std::string backing_name; +}; + +struct ViewModelInfo +{ + std::string name; + std::vector properties; +}; + struct ArtboardData { std::string artboard_name; @@ -83,6 +115,8 @@ struct RiveFileData std::string riv_kebab_case; std::vector artboards; std::vector assets; + std::vector enums; + std::vector view_models; }; // Helper function to convert a string to the specified case style @@ -124,16 +158,16 @@ std::string toCaseHelper(const std::string &str, CaseStyle style) { switch (style) { - case CaseStyle::Camel: - case CaseStyle::Pascal: - capitalizeNext = true; - break; - case CaseStyle::Snake: - result << '_'; - break; - case CaseStyle::Kebab: - result << '-'; - break; + case CaseStyle::Camel: + case CaseStyle::Pascal: + capitalizeNext = true; + break; + case CaseStyle::Snake: + result << '_'; + break; + case CaseStyle::Kebab: + result << '-'; + break; } } } @@ -152,7 +186,13 @@ std::string toCaseHelper(const std::string &str, CaseStyle style) std::string toCamelCase(const std::string &str) { - return toCaseHelper(str, CaseStyle::Camel); + std::string result = toCaseHelper(str, CaseStyle::Camel); + // Handle Dart reserved keywords + if (result == "with" || result == "class" || result == "enum" || result == "var" || result == "const" || result == "final" || result == "static" || result == "void" || result == "int" || result == "double" || result == "bool" || result == "String" || result == "List" || result == "Map" || result == "dynamic" || result == "null" || result == "true" || result == "false") + { + result = result + "Value"; + } + return result; } std::string toPascalCase(const std::string &str) @@ -170,23 +210,39 @@ std::string toKebabCase(const std::string &str) return toCaseHelper(str, CaseStyle::Kebab); } -std::string sanitizeString(const std::string& input) { +std::string sanitizeString(const std::string &input) +{ std::string output; - for (char c : input) { - switch (c) { - case '\n': output += "\\n"; break; - case '\r': output += "\\r"; break; - case '\t': output += "\\t"; break; - case '\"': output += "\\\""; break; - case '\\': output += "\\\\"; break; - default: - if (std::isprint(c)) { - output += c; - } else { - char hex[7]; - std::snprintf(hex, sizeof(hex), "\\u%04x", static_cast(c)); - output += hex; - } + for (char c : input) + { + switch (c) + { + case '\n': + output += "\\n"; + break; + case '\r': + output += "\\r"; + break; + case '\t': + output += "\\t"; + break; + case '\"': + output += "\\\""; + break; + case '\\': + output += "\\\\"; + break; + default: + if (std::isprint(c)) + { + output += c; + } + else + { + char hex[7]; + std::snprintf(hex, sizeof(hex), "\\u%04x", static_cast(c)); + output += hex; + } } } return output; @@ -242,38 +298,39 @@ std::vector>> get_state_machines_f for (int j = 0; j < inputCount; j++) { auto input = stateMachine->input(j); - + std::string inputType; std::string defaultValue; - + // Determine the input type and default value - switch (input->inputCoreType()) { - case rive::StateMachineNumberBase::typeKey: - { - auto smiNumberInput = static_cast(input); - inputType = "number"; - defaultValue = std::to_string(smiNumberInput->value()); - break; - } - case rive::StateMachineTriggerBase::typeKey: - { - inputType = "trigger"; - defaultValue = "false"; - break; - } - case rive::StateMachineBoolBase::typeKey: - { - auto smiBoolInput = static_cast(input); - inputType = "boolean"; - defaultValue = smiBoolInput->value() ? "true" : "false"; - break; - } - default: - { - inputType = "unknown"; - defaultValue = ""; - break; - } + switch (input->inputCoreType()) + { + case rive::StateMachineNumberBase::typeKey: + { + auto smiNumberInput = static_cast(input); + inputType = "number"; + defaultValue = std::to_string(smiNumberInput->value()); + break; + } + case rive::StateMachineTriggerBase::typeKey: + { + inputType = "trigger"; + defaultValue = "false"; + break; + } + case rive::StateMachineBoolBase::typeKey: + { + auto smiBoolInput = static_cast(input); + inputType = "boolean"; + defaultValue = smiBoolInput->value() ? "true" : "false"; + break; + } + default: + { + inputType = "unknown"; + defaultValue = ""; + break; + } } inputs.push_back({input->name(), inputType, defaultValue}); @@ -319,57 +376,59 @@ std::string makeUnique(const std::string &base, std::unordered_set return uniqueName; } - template -void findAll(std::vector& results, rive::ArtboardInstance *artboard) +void findAll(std::vector &results, rive::ArtboardInstance *artboard) { for (auto object : artboard->objects()) { if (object != nullptr && object->is()) { - results.push_back(static_cast(object)); + results.push_back(static_cast(object)); } } } std::vector get_text_value_runs_from_artboard(rive::ArtboardInstance *artboard) { - std::vector text_value_runs; + std::vector text_value_runs; std::vector text_value_runs_info; findAll(text_value_runs, artboard); for (auto text_value_run : text_value_runs) { - if (!text_value_run->name().empty()) { + if (!text_value_run->name().empty()) + { text_value_runs_info.push_back({text_value_run->name(), text_value_run->text()}); } } return text_value_runs_info; } -std::vector get_nested_text_value_run_paths_from_artboard(rive::ArtboardInstance *artboard, const std::string& current_path = "") +std::vector get_nested_text_value_run_paths_from_artboard(rive::ArtboardInstance *artboard, const std::string ¤t_path = "") { std::vector nested_text_value_runs_info; auto count = artboard->nestedArtboards().size(); - - if (!current_path.empty()) { + if (!current_path.empty()) + { auto text_runs = get_text_value_runs_from_artboard(artboard); - for (const auto& text_run : text_runs) { + for (const auto &text_run : text_runs) + { nested_text_value_runs_info.push_back({text_run.name, current_path}); } } // Recursively process nested artboards - for (int i = 0; i < count; i++) { + for (int i = 0; i < count; i++) + { auto nested = artboard->nestedArtboards()[i]; auto nested_name = nested->name(); - if (!nested_name.empty()) + if (!nested_name.empty()) { // Only process nested artboards that have an exported name std::string new_path = current_path.empty() ? nested->name() : current_path + "/" + nested->name(); - + auto nested_results = get_nested_text_value_run_paths_from_artboard(nested->artboardInstance(), new_path); nested_text_value_runs_info.insert(nested_text_value_runs_info.end(), nested_results.begin(), nested_results.end()); } @@ -387,19 +446,20 @@ std::vector get_assets_from_file(rive::File *file) for (auto asset : assets) { std::string assetType; - switch (asset->coreType()) { - case rive::ImageAsset::typeKey: - assetType = "image"; - break; - case rive::FontAsset::typeKey: - assetType = "font"; - break; - case rive::AudioAsset::typeKey: - assetType = "audio"; - break; - default: - assetType = "unknown"; - break; + switch (asset->coreType()) + { + case rive::ImageAsset::typeKey: + assetType = "image"; + break; + case rive::FontAsset::typeKey: + assetType = "font"; + break; + case rive::AudioAsset::typeKey: + assetType = "audio"; + break; + default: + assetType = "unknown"; + break; } auto assetName = asset->name(); @@ -411,12 +471,44 @@ std::vector get_assets_from_file(rive::File *file) asset->fileExtension(), std::to_string(asset->assetId()), asset->cdnUuidStr(), - asset->cdnBaseUrl() - }); + asset->cdnBaseUrl()}); } return assetsInfo; } +std::string dataTypeToString(rive::DataType type) +{ + switch (type) + { + case rive::DataType::none: + return "none"; + case rive::DataType::string: + return "string"; + case rive::DataType::number: + return "number"; + case rive::DataType::boolean: + return "boolean"; + case rive::DataType::color: + return "color"; + case rive::DataType::list: + return "list"; + case rive::DataType::enumType: + return "enum"; + case rive::DataType::trigger: + return "trigger"; + case rive::DataType::viewModel: + return "viewModel"; + case rive::DataType::integer: + return "integer"; + case rive::DataType::symbolListIndex: + return "symbolListIndex"; + case rive::DataType::assetImage: + return "assetImage"; + default: + return "unknown"; + } +} + std::optional process_riv_file(const std::string &rive_file_path) { // Check if the file is empty @@ -443,6 +535,64 @@ std::optional process_riv_file(const std::string &rive_file_path) file_data.riv_kebab_case = toKebabCase(file_name_without_extension); file_data.assets = assets; + // Process enums + const auto &fileEnums = riveFile->enums(); + for (auto *dataEnum : fileEnums) + { + if (dataEnum) + { + EnumInfo enumInfo; + enumInfo.name = dataEnum->enumName(); + const auto &values = dataEnum->values(); + for (const auto *value : values) + { + enumInfo.values.push_back({value->key()}); + } + file_data.enums.push_back(enumInfo); + } + } + + // Process view models + for (size_t i = 0; i < riveFile->viewModelCount(); i++) + { + auto viewModel = riveFile->viewModelByIndex(i); + if (viewModel) + { + ViewModelInfo viewModelInfo; + viewModelInfo.name = viewModel->name(); + auto propertiesData = viewModel->properties(); + for (const auto &property : propertiesData) + { + if (property.type == rive::DataType::viewModel) + { + // TODO: this is a hack + auto nestedViewModel = viewModel->createInstance()->propertyViewModel(property.name); + auto vm = nestedViewModel->instance()->viewModel(); + viewModelInfo.properties.push_back({property.name, + dataTypeToString(property.type), + vm->name()}); + } + else if (property.type == rive::DataType::enumType) + { + // TODO: this is a hack + auto vmi = riveFile->createViewModelInstance(viewModel->name()); + auto enum_instance = static_cast(vmi->propertyValue(property.name)); + auto enumProperty = enum_instance->viewModelProperty()->as(); + auto enumName = enumProperty->dataEnum()->enumName(); + viewModelInfo.properties.push_back({property.name, + dataTypeToString(property.type), + enumName}); + } + else + { + viewModelInfo.properties.push_back({property.name, + dataTypeToString(property.type)}); + } + } + file_data.view_models.push_back(viewModelInfo); + } + } + std::unordered_set usedArtboardNames; auto artboardCount = riveFile->artboardCount(); @@ -569,6 +719,88 @@ int main(int argc, char *argv[]) riv_file_data["riv_kebab_case"] = file_data.riv_kebab_case; riv_file_data["last"] = (file_index == rive_file_data_list.size() - 1); + // Add enums to template data + std::vector enums; + for (size_t enum_index = 0; enum_index < file_data.enums.size(); enum_index++) + { + const auto &enum_info = file_data.enums[enum_index]; + kainjow::mustache::data enum_data; + enum_data["enum_name"] = enum_info.name; + enum_data["enum_camel_case"] = toCamelCase(enum_info.name); + enum_data["enum_pascal_case"] = toPascalCase(enum_info.name); + enum_data["enum_snake_case"] = toSnakeCase(enum_info.name); + enum_data["enum_kebab_case"] = toKebabCase(enum_info.name); + enum_data["last"] = (enum_index == file_data.enums.size() - 1); + + std::vector enum_values; + for (size_t value_index = 0; value_index < enum_info.values.size(); value_index++) + { + const auto &value = enum_info.values[value_index]; + kainjow::mustache::data value_data; + value_data["enum_value_key"] = value.key; + value_data["enum_value_camel_case"] = toCamelCase(value.key); + value_data["enum_value_pascal_case"] = toPascalCase(value.key); + value_data["enum_value_snake_case"] = toSnakeCase(value.key); + value_data["enum_value_kebab_case"] = toKebabCase(value.key); + value_data["last"] = (value_index == enum_info.values.size() - 1); + enum_values.push_back(value_data); + } + enum_data["enum_values"] = enum_values; + enums.push_back(enum_data); + } + riv_file_data["enums"] = enums; + + // Add view models to template data + std::vector view_models; + for (size_t vm_index = 0; vm_index < file_data.view_models.size(); vm_index++) + { + const auto &view_model = file_data.view_models[vm_index]; + kainjow::mustache::data view_model_data; + view_model_data["view_model_name"] = view_model.name; + view_model_data["view_model_camel_case"] = toCamelCase(view_model.name); + view_model_data["view_model_pascal_case"] = toPascalCase(view_model.name); + view_model_data["view_model_snake_case"] = toSnakeCase(view_model.name); + view_model_data["view_model_kebab_case"] = toKebabCase(view_model.name); + view_model_data["last"] = (vm_index == file_data.view_models.size() - 1); + + std::vector properties; + for (size_t prop_index = 0; prop_index < view_model.properties.size(); prop_index++) + { + const auto &property = view_model.properties[prop_index]; + kainjow::mustache::data property_data; + property_data["property_name"] = property.name; + property_data["property_camel_case"] = toCamelCase(property.name); + property_data["property_pascal_case"] = toPascalCase(property.name); + property_data["property_snake_case"] = toSnakeCase(property.name); + property_data["property_kebab_case"] = toKebabCase(property.name); + property_data["property_type"] = property.type; + + // Add property type information for the viewmodel template + kainjow::mustache::data property_type_data; + property_type_data.set("is_view_model", property.type == "viewModel"); + property_type_data.set("is_enum", property.type == "enum"); + property_type_data.set("is_string", property.type == "string"); + property_type_data.set("is_number", property.type == "number"); + property_type_data.set("is_integer", property.type == "integer"); + property_type_data.set("is_boolean", property.type == "boolean"); + property_type_data.set("is_color", property.type == "color"); + property_type_data.set("is_list", property.type == "list"); + property_type_data.set("is_trigger", property.type == "trigger"); + property_type_data.set("backing_name", property.backing_name); + property_type_data.set("backing_camel_case", toCamelCase(property.backing_name)); + property_type_data.set("backing_pascal_case", toPascalCase(property.backing_name)); + property_type_data.set("backing_snake_case", toSnakeCase(property.backing_name)); + property_type_data.set("backing_kebab_case", toKebabCase(property.backing_name)); + property_data.set("property_type", property_type_data); + + property_data["last"] = (prop_index == view_model.properties.size() - 1); + properties.push_back(property_data); + } + view_model_data["properties"] = properties; + view_models.push_back(view_model_data); + } + riv_file_data["view_models"] = view_models; + std::vector assets; for (size_t asset_index = 0; asset_index < file_data.assets.size(); asset_index++) { @@ -690,7 +922,11 @@ int main(int argc, char *argv[]) } riv_file_data["artboards"] = artboard_list; - riv_file_list.push_back(riv_file_data); + // Only add the section if there are view models or enums + if (!view_models.empty() || !enums.empty()) + { + riv_file_list.push_back(riv_file_data); + } } template_data["generated_file_name"] = generated_file_name; diff --git a/templates/json_template.mustache b/templates/json_template.mustache index 3466888..1aaf020 100644 --- a/templates/json_template.mustache +++ b/templates/json_template.mustache @@ -13,6 +13,33 @@ }{{^last}},{{/last}} {{/assets}} }, + "enums": { + {{#enums}} + "{{enum_camel_case}}": { + "name": "{{enum_name}}", + "values": { + {{#enum_values}} + "{{enum_value_camel_case}}": "{{enum_value_key}}"{{^last}},{{/last}} + {{/enum_values}} + } + }{{^last}},{{/last}} + {{/enums}} + }, + "viewModels": { + {{#view_models}} + "{{view_model_camel_case}}": { + "name": "{{view_model_name}}", + "properties": { + {{#properties}} + "{{property_camel_case}}": { + "name": "{{property_name}}", + "type": "{{property_type}}" + }{{^last}},{{/last}} + {{/properties}} + } + }{{^last}},{{/last}} + {{/view_models}} + }, "artboards": { {{#artboards}} "{{artboard_camel_case}}": { diff --git a/templates/viewmodel_template.mustache b/templates/viewmodel_template.mustache new file mode 100644 index 0000000..bbaecb7 --- /dev/null +++ b/templates/viewmodel_template.mustache @@ -0,0 +1,63 @@ +// {{generated_file_name}}_viewmodels.dart + +// ignore_for_file: lines_longer_than_80_chars, unused_field + +import 'dart:ui'; +import 'package:rive_native/rive_native.dart' as rive; + +/// ViewModel classes for Rive files +{{#riv_files}} +// ---------------------- +// {{riv_camel_case}} ViewModels +// ---------------------- + +{{#enums}} +/// Enum for {{enum_name}} +enum {{enum_pascal_case}}Enum { + {{#enum_values}} + {{enum_value_camel_case}}('{{enum_value_key}}'){{^last}},{{/last}}{{#last}};{{/last}} + {{/enum_values}} + + final String value; + const {{enum_pascal_case}}Enum(this.value); + + @override + String toString() => value; +} +{{/enums}} + +{{#view_models}} +/// ViewModel class for {{view_model_name}} +class {{view_model_pascal_case}} { + final rive.ViewModelInstance instance; + {{#properties}} + {{#property_type}}{{#is_view_model}}final {{backing_pascal_case}} {{property_camel_case}};{{/is_view_model}}{{/property_type}}{{#property_type}}final {{#is_view_model}}rive.ViewModelInstance{{/is_view_model}}{{#is_enum}}rive.ViewModelInstanceEnum{{/is_enum}}{{#is_string}}rive.ViewModelInstanceString{{/is_string}}{{#is_number}}rive.ViewModelInstanceNumber{{/is_number}}{{#is_boolean}}rive.ViewModelInstanceBoolean{{/is_boolean}}{{#is_color}}rive.ViewModelInstanceColor{{/is_color}}{{#is_trigger}}rive.ViewModelInstanceTrigger{{/is_trigger}} _{{property_camel_case}}Property;{{/property_type}} + {{/properties}} + + {{view_model_pascal_case}}(this.instance): {{#properties}} + {{#property_type}}{{#is_view_model}}{{property_camel_case}} = {{backing_pascal_case}}(instance.viewModel('{{property_name}}')!),{{/is_view_model}}{{/property_type}} + {{#property_type}} + _{{property_camel_case}}Property = instance.{{#is_view_model}}viewModel{{/is_view_model}}{{#is_enum}}enumerator{{/is_enum}}{{#is_string}}string{{/is_string}}{{#is_number}}number{{/is_number}}{{#is_boolean}}boolean{{/is_boolean}}{{#is_color}}color{{/is_color}}{{#is_trigger}}trigger{{/is_trigger}}('{{property_name}}')!{{^last}},{{/last}}{{#last}};{{/last}} + {{/property_type}} + {{/properties}} + + {{#properties}} + {{#property_type}}{{#is_enum}}{{backing_pascal_case}}Enum get {{property_camel_case}} => {{backing_pascal_case}}Enum.values.byName(_{{property_camel_case}}Property.value.toLowerCase()); + set {{property_camel_case}}({{backing_pascal_case}}Enum value) => _{{property_camel_case}}Property.value = value.value;{{/is_enum}}{{#is_string}}String get {{property_camel_case}} => _{{property_camel_case}}Property.value; + set {{property_camel_case}}(String value) => _{{property_camel_case}}Property.value = value;{{/is_string}}{{#is_number}}double get {{property_camel_case}} => _{{property_camel_case}}Property.value; + set {{property_camel_case}}(double value) => _{{property_camel_case}}Property.value = value;{{/is_number}}{{#is_boolean}}bool get {{property_camel_case}} => _{{property_camel_case}}Property.value; + set {{property_camel_case}}(bool value) => _{{property_camel_case}}Property.value = value;{{/is_boolean}}{{#is_color}}Color get {{property_camel_case}} => _{{property_camel_case}}Property.value; + set {{property_camel_case}}(Color value) => _{{property_camel_case}}Property.value = value;{{/is_color}}{{#is_list}}List get {{property_camel_case}} => _{{property_camel_case}}Property.value; + set {{property_camel_case}}(List value) => _{{property_camel_case}}Property.value = value;{{/is_list}}{{#is_trigger}}void {{property_camel_case}}() => _{{property_camel_case}}Property.trigger();{{/is_trigger}}{{/property_type}} + {{/properties}} + + Map toJson() { + return { + {{#properties}} + {{#property_type}}{{#is_view_model}}'{{property_name}}': {{property_camel_case}}.toJson(),{{/is_view_model}}{{#is_enum}}'{{property_name}}': {{property_camel_case}}.value,{{/is_enum}}{{#is_string}}'{{property_name}}': {{property_camel_case}},{{/is_string}}{{#is_number}}'{{property_name}}': {{property_camel_case}},{{/is_number}}{{#is_boolean}}'{{property_name}}': {{property_camel_case}},{{/is_boolean}}{{#is_color}}'{{property_name}}': {{property_camel_case}}.value,{{/is_color}}{{#is_list}}'{{property_name}}': {{property_camel_case}},{{/is_list}}{{/property_type}} + {{/properties}} + }; + } +} +{{/view_models}} +{{/riv_files}} From 3bb039073559758e2de27d29bba5d0aca2b570ab Mon Sep 17 00:00:00 2001 From: CI Date: Fri, 13 Jun 2025 18:59:23 +0200 Subject: [PATCH 2/7] chore: clang formatting --- .clang-format | 83 +++++++ src/main.cpp | 610 +++++++++++++++++++++++++++++--------------------- 2 files changed, 444 insertions(+), 249 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b82c169 --- /dev/null +++ b/.clang-format @@ -0,0 +1,83 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +TabWidth: 4 +IndentCaseLabels: true +Language: Cpp +# Force pointers to the type for C++. +DerivePointerAlignment: false +PointerAlignment: Left +NamespaceIndentation: None +AccessModifierOffset: -4 +BreakConstructorInitializers: AfterColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ColumnLimit: 80 +BinPackArguments: false +BinPackParameters: false +AlignAfterOpenBracket: Align +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortCaseLabelsOnASingleLine: false +PenaltyReturnTypeOnItsOwnLine: 9999 +PenaltyExcessCharacter: 9999 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 999 +BreakBeforeBraces: Custom +SortIncludes: false +BraceWrapping: + AfterEnum: true + AfterClass: true + AfterControlStatement: true + AfterNamespace: true + AfterFunction: true + AfterStruct: true + AfterObjCDeclaration: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + AfterCaseLabel: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +--- +Language: ObjC +# Force pointers to the type for C++. +DerivePointerAlignment: false +IndentWidth: 4 +IndentCaseLabels: True +PointerAlignment: Left +NamespaceIndentation: None +AccessModifierOffset: -4 +BreakConstructorInitializers: AfterColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ColumnLimit: 80 +BinPackArguments: false +BinPackParameters: false +AlignAfterOpenBracket: Align +BreakBeforeBraces: Custom +SortIncludes: false +PenaltyReturnTypeOnItsOwnLine: 9999 +PenaltyExcessCharacter: 9999 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 999 +BraceWrapping: + AfterEnum: true + AfterClass: true + AfterControlStatement: true + AfterNamespace: true + AfterFunction: true + AfterStruct: true + AfterObjCDeclaration: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + AfterCaseLabel: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false diff --git a/src/main.cpp b/src/main.cpp index 057cd8b..b33659c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,30 @@ +#include #include -#include #include +#include #include +#include +#include +#include + #include "CLIUTILS/CLI11.hpp" +#include "default_template.h" #include "kainjow/mustache.hpp" -#include "rive/file.hpp" #include "rive/animation/linear_animation_instance.hpp" -#include "rive/animation/state_machine_instance.hpp" #include "rive/animation/state_machine_input_instance.hpp" -#include "rive/assets/image_asset.hpp" -#include "rive/assets/font_asset.hpp" +#include "rive/animation/state_machine_instance.hpp" #include "rive/assets/audio_asset.hpp" +#include "rive/assets/font_asset.hpp" +#include "rive/assets/image_asset.hpp" +#include "rive/file.hpp" #include "rive/generated/animation/state_machine_bool_base.hpp" #include "rive/generated/animation/state_machine_number_base.hpp" #include "rive/generated/animation/state_machine_trigger_base.hpp" -#include "rive/viewmodel/runtime/viewmodel_runtime.hpp" #include "rive/viewmodel/data_enum.hpp" #include "rive/viewmodel/data_enum_value.hpp" +#include "rive/viewmodel/runtime/viewmodel_runtime.hpp" #include "rive/viewmodel/viewmodel_property_enum.hpp" #include "utils/no_op_factory.hpp" -#include -#include -#include -#include -#include "default_template.h" const std::string generated_file_name = "rive_generated"; @@ -120,7 +121,7 @@ struct RiveFileData }; // Helper function to convert a string to the specified case style -std::string toCaseHelper(const std::string &str, CaseStyle style) +std::string toCaseHelper(const std::string& str, CaseStyle style) { std::stringstream result; bool capitalizeNext = (style == CaseStyle::Pascal); @@ -148,7 +149,8 @@ std::string toCaseHelper(const std::string &str, CaseStyle style) } else { - result << (style == CaseStyle::Pascal ? c : (char)std::tolower(c)); + result << (style == CaseStyle::Pascal ? c + : (char)std::tolower(c)); } firstChar = false; } @@ -158,16 +160,16 @@ std::string toCaseHelper(const std::string &str, CaseStyle style) { switch (style) { - case CaseStyle::Camel: - case CaseStyle::Pascal: - capitalizeNext = true; - break; - case CaseStyle::Snake: - result << '_'; - break; - case CaseStyle::Kebab: - result << '-'; - break; + case CaseStyle::Camel: + case CaseStyle::Pascal: + capitalizeNext = true; + break; + case CaseStyle::Snake: + result << '_'; + break; + case CaseStyle::Kebab: + result << '-'; + break; } } } @@ -184,65 +186,73 @@ std::string toCaseHelper(const std::string &str, CaseStyle style) return finalResult; } -std::string toCamelCase(const std::string &str) +std::string toCamelCase(const std::string& str) { std::string result = toCaseHelper(str, CaseStyle::Camel); // Handle Dart reserved keywords - if (result == "with" || result == "class" || result == "enum" || result == "var" || result == "const" || result == "final" || result == "static" || result == "void" || result == "int" || result == "double" || result == "bool" || result == "String" || result == "List" || result == "Map" || result == "dynamic" || result == "null" || result == "true" || result == "false") + if (result == "with" || result == "class" || result == "enum" || + result == "var" || result == "const" || result == "final" || + result == "static" || result == "void" || result == "int" || + result == "double" || result == "bool" || result == "String" || + result == "List" || result == "Map" || result == "dynamic" || + result == "null" || result == "true" || result == "false") { result = result + "Value"; } return result; } -std::string toPascalCase(const std::string &str) +std::string toPascalCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Pascal); } -std::string toSnakeCase(const std::string &str) +std::string toSnakeCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Snake); } -std::string toKebabCase(const std::string &str) +std::string toKebabCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Kebab); } -std::string sanitizeString(const std::string &input) +std::string sanitizeString(const std::string& input) { std::string output; for (char c : input) { switch (c) { - case '\n': - output += "\\n"; - break; - case '\r': - output += "\\r"; - break; - case '\t': - output += "\\t"; - break; - case '\"': - output += "\\\""; - break; - case '\\': - output += "\\\\"; - break; - default: - if (std::isprint(c)) - { - output += c; - } - else - { - char hex[7]; - std::snprintf(hex, sizeof(hex), "\\u%04x", static_cast(c)); - output += hex; - } + case '\n': + output += "\\n"; + break; + case '\r': + output += "\\r"; + break; + case '\t': + output += "\\t"; + break; + case '\"': + output += "\\\""; + break; + case '\\': + output += "\\\\"; + break; + default: + if (std::isprint(c)) + { + output += c; + } + else + { + char hex[7]; + std::snprintf(hex, + sizeof(hex), + "\\u%04x", + static_cast(c)); + output += hex; + } } } return output; @@ -250,7 +260,7 @@ std::string sanitizeString(const std::string &input) static std::unique_ptr open_file(const char name[]) { - FILE *f = fopen(name, "rb"); + FILE* f = fopen(name, "rb"); if (!f) { return nullptr; @@ -272,7 +282,8 @@ static std::unique_ptr open_file(const char name[]) return rive::File::import(bytes, &gFactory); } -static std::vector get_animations_from_artboard(rive::ArtboardInstance *artboard) +static std::vector get_animations_from_artboard( + rive::ArtboardInstance* artboard) { std::vector animations; auto animationCount = artboard->animationCount(); @@ -284,7 +295,8 @@ static std::vector get_animations_from_artboard(rive::ArtboardInsta return animations; } -std::vector>> get_state_machines_from_artboard(rive::ArtboardInstance *artboard) +std::vector>> +get_state_machines_from_artboard(rive::ArtboardInstance* artboard) { std::vector>> state_machines; auto stateMachineCount = artboard->stateMachineCount(); @@ -305,32 +317,32 @@ std::vector>> get_state_machines_f // Determine the input type and default value switch (input->inputCoreType()) { - case rive::StateMachineNumberBase::typeKey: - { - auto smiNumberInput = static_cast(input); - inputType = "number"; - defaultValue = std::to_string(smiNumberInput->value()); - break; - } - case rive::StateMachineTriggerBase::typeKey: - { - inputType = "trigger"; - defaultValue = "false"; - break; - } - case rive::StateMachineBoolBase::typeKey: - { - auto smiBoolInput = static_cast(input); - inputType = "boolean"; - defaultValue = smiBoolInput->value() ? "true" : "false"; - break; - } - default: - { - inputType = "unknown"; - defaultValue = ""; - break; - } + case rive::StateMachineNumberBase::typeKey: + { + auto smiNumberInput = static_cast(input); + inputType = "number"; + defaultValue = std::to_string(smiNumberInput->value()); + break; + } + case rive::StateMachineTriggerBase::typeKey: + { + inputType = "trigger"; + defaultValue = "false"; + break; + } + case rive::StateMachineBoolBase::typeKey: + { + auto smiBoolInput = static_cast(input); + inputType = "boolean"; + defaultValue = smiBoolInput->value() ? "true" : "false"; + break; + } + default: + { + inputType = "unknown"; + defaultValue = ""; + break; + } } inputs.push_back({input->name(), inputType, defaultValue}); @@ -341,13 +353,13 @@ std::vector>> get_state_machines_f return state_machines; } -std::vector find_riv_files(const std::string &path) +std::vector find_riv_files(const std::string& path) { std::vector riv_files; if (std::filesystem::is_directory(path)) { - for (const auto &entry : std::filesystem::directory_iterator(path)) + for (const auto& entry : std::filesystem::directory_iterator(path)) { if (entry.path().extension() == ".riv") { @@ -363,7 +375,8 @@ std::vector find_riv_files(const std::string &path) return riv_files; } -std::string makeUnique(const std::string &base, std::unordered_set &usedNames) +std::string makeUnique(const std::string& base, + std::unordered_set& usedNames) { std::string uniqueName = base; int counter = 1; @@ -377,20 +390,21 @@ std::string makeUnique(const std::string &base, std::unordered_set } template -void findAll(std::vector &results, rive::ArtboardInstance *artboard) +void findAll(std::vector& results, rive::ArtboardInstance* artboard) { for (auto object : artboard->objects()) { if (object != nullptr && object->is()) { - results.push_back(static_cast(object)); + results.push_back(static_cast(object)); } } } -std::vector get_text_value_runs_from_artboard(rive::ArtboardInstance *artboard) +std::vector get_text_value_runs_from_artboard( + rive::ArtboardInstance* artboard) { - std::vector text_value_runs; + std::vector text_value_runs; std::vector text_value_runs_info; findAll(text_value_runs, artboard); @@ -399,13 +413,17 @@ std::vector get_text_value_runs_from_artboard(rive::ArtboardIn { if (!text_value_run->name().empty()) { - text_value_runs_info.push_back({text_value_run->name(), text_value_run->text()}); + text_value_runs_info.push_back( + {text_value_run->name(), text_value_run->text()}); } } return text_value_runs_info; } -std::vector get_nested_text_value_run_paths_from_artboard(rive::ArtboardInstance *artboard, const std::string ¤t_path = "") +std::vector +get_nested_text_value_run_paths_from_artboard( + rive::ArtboardInstance* artboard, + const std::string& current_path = "") { std::vector nested_text_value_runs_info; auto count = artboard->nestedArtboards().size(); @@ -413,9 +431,10 @@ std::vector get_nested_text_value_run_paths_from_artboar if (!current_path.empty()) { auto text_runs = get_text_value_runs_from_artboard(artboard); - for (const auto &text_run : text_runs) + for (const auto& text_run : text_runs) { - nested_text_value_runs_info.push_back({text_run.name, current_path}); + nested_text_value_runs_info.push_back( + {text_run.name, current_path}); } } @@ -427,17 +446,24 @@ std::vector get_nested_text_value_run_paths_from_artboar if (!nested_name.empty()) { // Only process nested artboards that have an exported name - std::string new_path = current_path.empty() ? nested->name() : current_path + "/" + nested->name(); - - auto nested_results = get_nested_text_value_run_paths_from_artboard(nested->artboardInstance(), new_path); - nested_text_value_runs_info.insert(nested_text_value_runs_info.end(), nested_results.begin(), nested_results.end()); + std::string new_path = current_path.empty() + ? nested->name() + : current_path + "/" + nested->name(); + + auto nested_results = get_nested_text_value_run_paths_from_artboard( + nested->artboardInstance(), + new_path); + nested_text_value_runs_info.insert( + nested_text_value_runs_info.end(), + nested_results.begin(), + nested_results.end()); } } return nested_text_value_runs_info; } -std::vector get_assets_from_file(rive::File *file) +std::vector get_assets_from_file(rive::File* file) { std::vector assetsInfo; std::unordered_set usedAssetNames; @@ -448,30 +474,29 @@ std::vector get_assets_from_file(rive::File *file) std::string assetType; switch (asset->coreType()) { - case rive::ImageAsset::typeKey: - assetType = "image"; - break; - case rive::FontAsset::typeKey: - assetType = "font"; - break; - case rive::AudioAsset::typeKey: - assetType = "audio"; - break; - default: - assetType = "unknown"; - break; + case rive::ImageAsset::typeKey: + assetType = "image"; + break; + case rive::FontAsset::typeKey: + assetType = "font"; + break; + case rive::AudioAsset::typeKey: + assetType = "audio"; + break; + default: + assetType = "unknown"; + break; } auto assetName = asset->name(); auto uniqueAssetName = makeUnique(assetName, usedAssetNames); - assetsInfo.push_back(AssetInfo{ - uniqueAssetName, - assetType, - asset->fileExtension(), - std::to_string(asset->assetId()), - asset->cdnUuidStr(), - asset->cdnBaseUrl()}); + assetsInfo.push_back(AssetInfo{uniqueAssetName, + assetType, + asset->fileExtension(), + std::to_string(asset->assetId()), + asset->cdnUuidStr(), + asset->cdnBaseUrl()}); } return assetsInfo; } @@ -480,48 +505,50 @@ std::string dataTypeToString(rive::DataType type) { switch (type) { - case rive::DataType::none: - return "none"; - case rive::DataType::string: - return "string"; - case rive::DataType::number: - return "number"; - case rive::DataType::boolean: - return "boolean"; - case rive::DataType::color: - return "color"; - case rive::DataType::list: - return "list"; - case rive::DataType::enumType: - return "enum"; - case rive::DataType::trigger: - return "trigger"; - case rive::DataType::viewModel: - return "viewModel"; - case rive::DataType::integer: - return "integer"; - case rive::DataType::symbolListIndex: - return "symbolListIndex"; - case rive::DataType::assetImage: - return "assetImage"; - default: - return "unknown"; + case rive::DataType::none: + return "none"; + case rive::DataType::string: + return "string"; + case rive::DataType::number: + return "number"; + case rive::DataType::boolean: + return "boolean"; + case rive::DataType::color: + return "color"; + case rive::DataType::list: + return "list"; + case rive::DataType::enumType: + return "enum"; + case rive::DataType::trigger: + return "trigger"; + case rive::DataType::viewModel: + return "viewModel"; + case rive::DataType::integer: + return "integer"; + case rive::DataType::symbolListIndex: + return "symbolListIndex"; + case rive::DataType::assetImage: + return "assetImage"; + default: + return "unknown"; } } -std::optional process_riv_file(const std::string &rive_file_path) +std::optional process_riv_file(const std::string& rive_file_path) { // Check if the file is empty if (std::filesystem::is_empty(rive_file_path)) { - std::cerr << "Error: Rive file is empty: " << rive_file_path << std::endl; + std::cerr << "Error: Rive file is empty: " << rive_file_path + << std::endl; return std::nullopt; } auto riveFile = open_file(rive_file_path.c_str()); if (!riveFile) { - std::cerr << "Error: Failed to parse Rive file: " << rive_file_path << std::endl; + std::cerr << "Error: Failed to parse Rive file: " << rive_file_path + << std::endl; return std::nullopt; } @@ -536,15 +563,15 @@ std::optional process_riv_file(const std::string &rive_file_path) file_data.assets = assets; // Process enums - const auto &fileEnums = riveFile->enums(); - for (auto *dataEnum : fileEnums) + const auto& fileEnums = riveFile->enums(); + for (auto* dataEnum : fileEnums) { if (dataEnum) { EnumInfo enumInfo; enumInfo.name = dataEnum->enumName(); - const auto &values = dataEnum->values(); - for (const auto *value : values) + const auto& values = dataEnum->values(); + for (const auto* value : values) { enumInfo.values.push_back({value->key()}); } @@ -561,32 +588,40 @@ std::optional process_riv_file(const std::string &rive_file_path) ViewModelInfo viewModelInfo; viewModelInfo.name = viewModel->name(); auto propertiesData = viewModel->properties(); - for (const auto &property : propertiesData) + for (const auto& property : propertiesData) { if (property.type == rive::DataType::viewModel) { // TODO: this is a hack - auto nestedViewModel = viewModel->createInstance()->propertyViewModel(property.name); + auto nestedViewModel = + viewModel->createInstance()->propertyViewModel( + property.name); auto vm = nestedViewModel->instance()->viewModel(); - viewModelInfo.properties.push_back({property.name, - dataTypeToString(property.type), - vm->name()}); + viewModelInfo.properties.push_back( + {property.name, + dataTypeToString(property.type), + vm->name()}); } else if (property.type == rive::DataType::enumType) { // TODO: this is a hack - auto vmi = riveFile->createViewModelInstance(viewModel->name()); - auto enum_instance = static_cast(vmi->propertyValue(property.name)); - auto enumProperty = enum_instance->viewModelProperty()->as(); + auto vmi = + riveFile->createViewModelInstance(viewModel->name()); + auto enum_instance = + static_cast( + vmi->propertyValue(property.name)); + auto enumProperty = enum_instance->viewModelProperty() + ->as(); auto enumName = enumProperty->dataEnum()->enumName(); - viewModelInfo.properties.push_back({property.name, - dataTypeToString(property.type), - enumName}); + viewModelInfo.properties.push_back( + {property.name, + dataTypeToString(property.type), + enumName}); } else { - viewModelInfo.properties.push_back({property.name, - dataTypeToString(property.type)}); + viewModelInfo.properties.push_back( + {property.name, dataTypeToString(property.type)}); } } file_data.view_models.push_back(viewModelInfo); @@ -607,31 +642,46 @@ std::optional process_riv_file(const std::string &rive_file_path) std::string artboard_kebab_case = toKebabCase(artboard_name); // Ensure unique artboard variable names - artboard_camel_case = makeUnique(artboard_camel_case, usedArtboardNames); - - std::vector animations = get_animations_from_artboard(artboard.get()); - std::vector>> state_machines = get_state_machines_from_artboard(artboard.get()); - std::vector text_value_runs = get_text_value_runs_from_artboard(artboard.get()); - std::vector nested_text_value_runs = get_nested_text_value_run_paths_from_artboard(artboard.get()); - - file_data.artboards.push_back({artboard_name, artboard_pascal_case, artboard_camel_case, artboard_snake_case, artboard_kebab_case, animations, state_machines, text_value_runs, nested_text_value_runs}); + artboard_camel_case = + makeUnique(artboard_camel_case, usedArtboardNames); + + std::vector animations = + get_animations_from_artboard(artboard.get()); + std::vector>> + state_machines = get_state_machines_from_artboard(artboard.get()); + std::vector text_value_runs = + get_text_value_runs_from_artboard(artboard.get()); + std::vector nested_text_value_runs = + get_nested_text_value_run_paths_from_artboard(artboard.get()); + + file_data.artboards.push_back({artboard_name, + artboard_pascal_case, + artboard_camel_case, + artboard_snake_case, + artboard_kebab_case, + animations, + state_machines, + text_value_runs, + nested_text_value_runs}); } return file_data; } -std::optional read_template_file(const std::string &path) +std::optional read_template_file(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { - std::cerr << "Warning: Unable to open template file: " << path << std::endl; + std::cerr << "Warning: Unable to open template file: " << path + << std::endl; return std::nullopt; } - return std::string(std::istreambuf_iterator(file), std::istreambuf_iterator()); + return std::string(std::istreambuf_iterator(file), + std::istreambuf_iterator()); } -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { CLI::App app{"Rive Code Generator"}; @@ -640,7 +690,9 @@ int main(int argc, char *argv[]) std::string template_path; Language language = Language::Dart; // Default to Dart - app.add_option("-i, --input", input_path, "Path to Rive file or directory containing Rive files") + app.add_option("-i, --input", + input_path, + "Path to Rive file or directory containing Rive files") ->required() ->check(CLI::ExistingFile | CLI::ExistingDirectory); @@ -649,11 +701,13 @@ int main(int argc, char *argv[]) app.add_option("-t,--template", template_path, "Custom template file path"); - app.add_option("-l, --language", language, "Programming language for code generation") - ->transform(CLI::CheckedTransformer(std::map{ - {"dart", Language::Dart}, - {"js", Language::JavaScript}}, - CLI::ignore_case)); + app.add_option("-l, --language", + language, + "Programming language for code generation") + ->transform(CLI::CheckedTransformer( + std::map{{"dart", Language::Dart}, + {"js", Language::JavaScript}}, + CLI::ignore_case)); CLI11_PARSE(app, argc, argv); @@ -664,11 +718,13 @@ int main(int argc, char *argv[]) if (custom_template) { template_str = *custom_template; - std::cout << "Using custom template from: " << template_path << std::endl; + std::cout << "Using custom template from: " << template_path + << std::endl; } else { - // TODO: This is probably not needed. Or can have a safety to fallback to the language specified + // TODO: This is probably not needed. Or can have a safety to + // fallback to the language specified std::cout << "Falling back to default template." << std::endl; template_str = default_templates::DEFAULT_DART_TEMPLATE; } @@ -681,7 +737,8 @@ int main(int argc, char *argv[]) } else if (language == Language::JavaScript) { - std::cout << "JavaScript code generation is not yet supported." << std::endl; + std::cout << "JavaScript code generation is not yet supported." + << std::endl; return 1; } } @@ -695,7 +752,7 @@ int main(int argc, char *argv[]) } std::vector rive_file_data_list; - for (const auto &riv_file : riv_files) + for (const auto& riv_file : riv_files) { auto result = process_riv_file(riv_file); if (result) @@ -709,9 +766,10 @@ int main(int argc, char *argv[]) kainjow::mustache::data template_data; std::vector riv_file_list; - for (size_t file_index = 0; file_index < rive_file_data_list.size(); file_index++) + for (size_t file_index = 0; file_index < rive_file_data_list.size(); + file_index++) { - const auto &file_data = rive_file_data_list[file_index]; + const auto& file_data = rive_file_data_list[file_index]; kainjow::mustache::data riv_file_data; riv_file_data["riv_pascal_case"] = file_data.riv_pascal_case; riv_file_data["riv_camel_case"] = file_data.riv_camel_case; @@ -721,9 +779,10 @@ int main(int argc, char *argv[]) // Add enums to template data std::vector enums; - for (size_t enum_index = 0; enum_index < file_data.enums.size(); enum_index++) + for (size_t enum_index = 0; enum_index < file_data.enums.size(); + enum_index++) { - const auto &enum_info = file_data.enums[enum_index]; + const auto& enum_info = file_data.enums[enum_index]; kainjow::mustache::data enum_data; enum_data["enum_name"] = enum_info.name; enum_data["enum_camel_case"] = toCamelCase(enum_info.name); @@ -733,16 +792,18 @@ int main(int argc, char *argv[]) enum_data["last"] = (enum_index == file_data.enums.size() - 1); std::vector enum_values; - for (size_t value_index = 0; value_index < enum_info.values.size(); value_index++) + for (size_t value_index = 0; value_index < enum_info.values.size(); + value_index++) { - const auto &value = enum_info.values[value_index]; + const auto& value = enum_info.values[value_index]; kainjow::mustache::data value_data; value_data["enum_value_key"] = value.key; value_data["enum_value_camel_case"] = toCamelCase(value.key); value_data["enum_value_pascal_case"] = toPascalCase(value.key); value_data["enum_value_snake_case"] = toSnakeCase(value.key); value_data["enum_value_kebab_case"] = toKebabCase(value.key); - value_data["last"] = (value_index == enum_info.values.size() - 1); + value_data["last"] = + (value_index == enum_info.values.size() - 1); enum_values.push_back(value_data); } enum_data["enum_values"] = enum_values; @@ -752,48 +813,69 @@ int main(int argc, char *argv[]) // Add view models to template data std::vector view_models; - for (size_t vm_index = 0; vm_index < file_data.view_models.size(); vm_index++) + for (size_t vm_index = 0; vm_index < file_data.view_models.size(); + vm_index++) { - const auto &view_model = file_data.view_models[vm_index]; + const auto& view_model = file_data.view_models[vm_index]; kainjow::mustache::data view_model_data; view_model_data["view_model_name"] = view_model.name; - view_model_data["view_model_camel_case"] = toCamelCase(view_model.name); - view_model_data["view_model_pascal_case"] = toPascalCase(view_model.name); - view_model_data["view_model_snake_case"] = toSnakeCase(view_model.name); - view_model_data["view_model_kebab_case"] = toKebabCase(view_model.name); - view_model_data["last"] = (vm_index == file_data.view_models.size() - 1); + view_model_data["view_model_camel_case"] = + toCamelCase(view_model.name); + view_model_data["view_model_pascal_case"] = + toPascalCase(view_model.name); + view_model_data["view_model_snake_case"] = + toSnakeCase(view_model.name); + view_model_data["view_model_kebab_case"] = + toKebabCase(view_model.name); + view_model_data["last"] = + (vm_index == file_data.view_models.size() - 1); std::vector properties; - for (size_t prop_index = 0; prop_index < view_model.properties.size(); prop_index++) + for (size_t prop_index = 0; + prop_index < view_model.properties.size(); + prop_index++) { - const auto &property = view_model.properties[prop_index]; + const auto& property = view_model.properties[prop_index]; kainjow::mustache::data property_data; property_data["property_name"] = property.name; - property_data["property_camel_case"] = toCamelCase(property.name); - property_data["property_pascal_case"] = toPascalCase(property.name); - property_data["property_snake_case"] = toSnakeCase(property.name); - property_data["property_kebab_case"] = toKebabCase(property.name); + property_data["property_camel_case"] = + toCamelCase(property.name); + property_data["property_pascal_case"] = + toPascalCase(property.name); + property_data["property_snake_case"] = + toSnakeCase(property.name); + property_data["property_kebab_case"] = + toKebabCase(property.name); property_data["property_type"] = property.type; // Add property type information for the viewmodel template kainjow::mustache::data property_type_data; - property_type_data.set("is_view_model", property.type == "viewModel"); + property_type_data.set("is_view_model", + property.type == "viewModel"); property_type_data.set("is_enum", property.type == "enum"); property_type_data.set("is_string", property.type == "string"); property_type_data.set("is_number", property.type == "number"); - property_type_data.set("is_integer", property.type == "integer"); - property_type_data.set("is_boolean", property.type == "boolean"); + property_type_data.set("is_integer", + property.type == "integer"); + property_type_data.set("is_boolean", + property.type == "boolean"); property_type_data.set("is_color", property.type == "color"); property_type_data.set("is_list", property.type == "list"); - property_type_data.set("is_trigger", property.type == "trigger"); + property_type_data.set("is_trigger", + property.type == "trigger"); property_type_data.set("backing_name", property.backing_name); - property_type_data.set("backing_camel_case", toCamelCase(property.backing_name)); - property_type_data.set("backing_pascal_case", toPascalCase(property.backing_name)); - property_type_data.set("backing_snake_case", toSnakeCase(property.backing_name)); - property_type_data.set("backing_kebab_case", toKebabCase(property.backing_name)); + property_type_data.set("backing_camel_case", + toCamelCase(property.backing_name)); + property_type_data.set("backing_pascal_case", + toPascalCase(property.backing_name)); + property_type_data.set("backing_snake_case", + toSnakeCase(property.backing_name)); + property_type_data.set("backing_kebab_case", + toKebabCase(property.backing_name)); property_data.set("property_type", property_type_data); - property_data["last"] = (prop_index == view_model.properties.size() - 1); + property_data["last"] = + (prop_index == view_model.properties.size() - 1); properties.push_back(property_data); } view_model_data["properties"] = properties; @@ -802,9 +884,10 @@ int main(int argc, char *argv[]) riv_file_data["view_models"] = view_models; std::vector assets; - for (size_t asset_index = 0; asset_index < file_data.assets.size(); asset_index++) + for (size_t asset_index = 0; asset_index < file_data.assets.size(); + asset_index++) { - const auto &asset = file_data.assets[asset_index]; + const auto& asset = file_data.assets[asset_index]; kainjow::mustache::data asset_data; asset_data["asset_name"] = asset.name; asset_data["asset_camel_case"] = toCamelCase(asset.name); @@ -821,22 +904,27 @@ int main(int argc, char *argv[]) riv_file_data["assets"] = assets; std::vector artboard_list; - for (size_t artboard_index = 0; artboard_index < file_data.artboards.size(); artboard_index++) + for (size_t artboard_index = 0; + artboard_index < file_data.artboards.size(); + artboard_index++) { - const auto &artboard = file_data.artboards[artboard_index]; + const auto& artboard = file_data.artboards[artboard_index]; kainjow::mustache::data artboard_data; artboard_data["artboard_name"] = artboard.artboard_name; - artboard_data["artboard_pascal_case"] = artboard.artboard_pascal_case; + artboard_data["artboard_pascal_case"] = + artboard.artboard_pascal_case; artboard_data["artboard_camel_case"] = artboard.artboard_camel_case; artboard_data["artboard_snake_case"] = artboard.artboard_snake_case; artboard_data["artboard_kebab_case"] = artboard.artboard_kebab_case; - artboard_data["last"] = (artboard_index == file_data.artboards.size() - 1); + artboard_data["last"] = + (artboard_index == file_data.artboards.size() - 1); std::unordered_set usedAnimationNames; std::vector animations; - for (size_t anim_index = 0; anim_index < artboard.animations.size(); anim_index++) + for (size_t anim_index = 0; anim_index < artboard.animations.size(); + anim_index++) { - const auto &animation = artboard.animations[anim_index]; + const auto& animation = artboard.animations[anim_index]; kainjow::mustache::data anim_data; auto unique_name = makeUnique(animation, usedAnimationNames); anim_data["animation_name"] = animation; @@ -844,30 +932,40 @@ int main(int argc, char *argv[]) anim_data["animation_pascal_case"] = toPascalCase(unique_name); anim_data["animation_snake_case"] = toSnakeCase(unique_name); anim_data["animation_kebab_case"] = toKebabCase(unique_name); - anim_data["last"] = (anim_index == artboard.animations.size() - 1); + anim_data["last"] = + (anim_index == artboard.animations.size() - 1); animations.push_back(anim_data); } artboard_data["animations"] = animations; std::unordered_set usedStateMachineNames; std::vector state_machines; - for (size_t sm_index = 0; sm_index < artboard.state_machines.size(); sm_index++) + for (size_t sm_index = 0; sm_index < artboard.state_machines.size(); + sm_index++) { - const auto &state_machine = artboard.state_machines[sm_index]; + const auto& state_machine = artboard.state_machines[sm_index]; kainjow::mustache::data state_machine_data; - auto unique_name = makeUnique(state_machine.first, usedStateMachineNames); + auto unique_name = + makeUnique(state_machine.first, usedStateMachineNames); state_machine_data["state_machine_name"] = state_machine.first; - state_machine_data["state_machine_camel_case"] = toCamelCase(unique_name); - state_machine_data["state_machine_pascal_case"] = toPascalCase(unique_name); - state_machine_data["state_machine_snake_case"] = toSnakeCase(unique_name); - state_machine_data["state_machine_kebab_case"] = toKebabCase(unique_name); - state_machine_data["last"] = (sm_index == artboard.state_machines.size() - 1); + state_machine_data["state_machine_camel_case"] = + toCamelCase(unique_name); + state_machine_data["state_machine_pascal_case"] = + toPascalCase(unique_name); + state_machine_data["state_machine_snake_case"] = + toSnakeCase(unique_name); + state_machine_data["state_machine_kebab_case"] = + toKebabCase(unique_name); + state_machine_data["last"] = + (sm_index == artboard.state_machines.size() - 1); std::unordered_set usedInputNames; std::vector inputs; - for (size_t input_index = 0; input_index < state_machine.second.size(); input_index++) + for (size_t input_index = 0; + input_index < state_machine.second.size(); + input_index++) { - const auto &input = state_machine.second[input_index]; + const auto& input = state_machine.second[input_index]; kainjow::mustache::data input_data; auto unique_name = makeUnique(input.name, usedInputNames); input_data["input_name"] = input.name; @@ -877,7 +975,8 @@ int main(int argc, char *argv[]) input_data["input_kebab_case"] = toKebabCase(unique_name); input_data["input_type"] = input.type; input_data["input_default_value"] = input.default_value; - input_data["last"] = (input_index == state_machine.second.size() - 1); + input_data["last"] = + (input_index == state_machine.second.size() - 1); inputs.push_back(input_data); } state_machine_data["inputs"] = inputs; @@ -888,31 +987,42 @@ int main(int argc, char *argv[]) std::unordered_set usedTextValueRunNames; std::vector text_value_runs; - for (size_t tvr_index = 0; tvr_index < artboard.text_value_runs.size(); tvr_index++) + for (size_t tvr_index = 0; + tvr_index < artboard.text_value_runs.size(); + tvr_index++) { - const auto &tvr = artboard.text_value_runs[tvr_index]; + const auto& tvr = artboard.text_value_runs[tvr_index]; kainjow::mustache::data tvr_data; auto unique_name = makeUnique(tvr.name, usedTextValueRunNames); tvr_data["text_value_run_name"] = tvr.name; - tvr_data["text_value_run_camel_case"] = toCamelCase(unique_name); - tvr_data["text_value_run_pascal_case"] = toPascalCase(unique_name); - tvr_data["text_value_run_snake_case"] = toSnakeCase(unique_name); - tvr_data["text_value_run_kebab_case"] = toKebabCase(unique_name); + tvr_data["text_value_run_camel_case"] = + toCamelCase(unique_name); + tvr_data["text_value_run_pascal_case"] = + toPascalCase(unique_name); + tvr_data["text_value_run_snake_case"] = + toSnakeCase(unique_name); + tvr_data["text_value_run_kebab_case"] = + toKebabCase(unique_name); tvr_data["text_value_run_default"] = tvr.default_value; - tvr_data["text_value_run_default_sanitized"] = sanitizeString(tvr.default_value); - tvr_data["last"] = (tvr_index == artboard.text_value_runs.size() - 1); + tvr_data["text_value_run_default_sanitized"] = + sanitizeString(tvr.default_value); + tvr_data["last"] = + (tvr_index == artboard.text_value_runs.size() - 1); text_value_runs.push_back(tvr_data); } artboard_data["text_value_runs"] = text_value_runs; std::vector nested_text_value_runs; - for (size_t ntvr_index = 0; ntvr_index < artboard.nested_text_value_runs.size(); ntvr_index++) + for (size_t ntvr_index = 0; + ntvr_index < artboard.nested_text_value_runs.size(); + ntvr_index++) { - const auto &ntvr = artboard.nested_text_value_runs[ntvr_index]; + const auto& ntvr = artboard.nested_text_value_runs[ntvr_index]; kainjow::mustache::data ntvr_data; ntvr_data["nested_text_value_run_name"] = ntvr.name; ntvr_data["nested_text_value_run_path"] = ntvr.path; - ntvr_data["last"] = (ntvr_index == artboard.nested_text_value_runs.size() - 1); + ntvr_data["last"] = + (ntvr_index == artboard.nested_text_value_runs.size() - 1); nested_text_value_runs.push_back(ntvr_data); } @@ -945,13 +1055,15 @@ int main(int argc, char *argv[]) output_path = std::filesystem::current_path() / output_path; } - // Create directories if they don't exist (this won't do anything if it's just a filename) + // Create directories if they don't exist (this won't do anything if it's + // just a filename) std::filesystem::create_directories(output_path.parent_path()); std::ofstream output_file(output_path); if (!output_file.is_open()) { - std::cerr << "Error: Unable to open output file: " << output_path << std::endl; + std::cerr << "Error: Unable to open output file: " << output_path + << std::endl; return 1; } output_file << result; From 0133ac5c41ae31c80434348fdc6b6f1fbb780f35 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Sun, 15 Jun 2025 14:13:01 +0200 Subject: [PATCH 3/7] chore: bump rive runtime submodule --- rive-runtime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rive-runtime b/rive-runtime index fc31e5e..1a9350e 160000 --- a/rive-runtime +++ b/rive-runtime @@ -1 +1 @@ -Subproject commit fc31e5e7572d17817463f12e13e1cf39f19aff84 +Subproject commit 1a9350e4ae1b7d0652757e9425c2b74a6c3b2992 From b1878094ca91a2598537196765a8a209e438ec98 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Sun, 15 Jun 2025 14:14:07 +0200 Subject: [PATCH 4/7] feat: add windows build scripts --- .gitignore | 1 + build/build.ps1 | 2 + build/build.sh | 191 +++++++++++++++++++++++------- build/premake5_code_generator.lua | 5 +- 4 files changed, 153 insertions(+), 46 deletions(-) create mode 100644 build/build.ps1 diff --git a/.gitignore b/.gitignore index 5222697..f31139a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # build build/out build/macosx +build/dependencies # examples examples/generated/ diff --git a/build/build.ps1 b/build/build.ps1 new file mode 100644 index 0000000..d77a780 --- /dev/null +++ b/build/build.ps1 @@ -0,0 +1,2 @@ +..\rive-runtime\build\setup_windows_dev.ps1 +bash ".\build.sh" @args \ No newline at end of file diff --git a/build/build.sh b/build/build.sh index cf41103..fcb6962 100755 --- a/build/build.sh +++ b/build/build.sh @@ -1,60 +1,161 @@ -#!/bin/sh -set -e -echo "Building rive_code_generator" - +#!/bin/bash +set -euo pipefail -source ../rive-runtime/dependencies/config_directories.sh -# if [[ "$OSTYPE" == "darwin"* ]]; then -# # macOS-specific code -# echo "Detected macOS. Running macOS build script..." -# source ../rive-runtime/dependencies/macosx/config_directories.sh -# else -# echo "This script currently only supports macOS." -# exit 1 -# fi +echo "Building rive_code_generator" CONFIG=debug +KIND= +CLEAN=false +RUN=false +DEV=false +RUNTIME_PATH="$PWD/../rive-runtime/build" for var in "$@"; do - if [[ $var = "release" ]]; then - CONFIG=release - fi + case "$var" in + --help|-h) + echo "Usage: ./build.sh [release] [clean] [run] [dev]" + exit 0 + ;; + esac done -if [[ ! -f "$DEPENDENCIES/bin/premake5" ]]; then - pushd $DEPENDENCIES_SCRIPTS - ./get_premake5.sh +for var in "$@"; do + case "$var" in + release) CONFIG=release ;; + clean) CLEAN=true ;; + run) RUN=true ;; + dev) DEV=true ;; + esac +done + +unameOut="$(uname -s)" +case "${unameOut}" in +Linux*) machine=linux ;; +Darwin*) machine=macosx ;; +MINGW*) machine=windows ;; +*) machine="unhandled:${unameOut}" ;; +esac +OS=$machine + +if [[ $OS == "windows" ]]; then + if ! command -v msbuild.exe &>/dev/null; then + powershell "./build.ps1" $@ + exit $? + fi +fi +if [[ $OS = "linux" ]]; then + LOCAL_ARCH=$('arch') + if [[ $LOCAL_ARCH == "aarch64" ]]; then + LINUX_ARCH=arm64 + else + LINUX_ARCH=x64 + fi +fi + +# Setup PREMAKE +download_premake() { + mkdir -p dependencies/bin + pushd dependencies/bin + echo Downloading Premake5 + if [[ $OS = "macosx" ]]; then + curl https://github.com/premake/premake-core/releases/download/v5.0.0-beta3/premake-5.0.0-beta3-macosx.tar.gz -L -o premake_macosx.tar.gz + # Export premake5 into bin + tar -xvf premake_macosx.tar.gz 2>/dev/null + # the zip for beta3 does not have x + chmod +x premake5 + # Delete downloaded archive + rm premake_macosx.tar.gz + elif [[ $OS = "windows" ]]; then + curl https://github.com/premake/premake-core/releases/download/v5.0.0-beta3/premake-5.0.0-beta3-windows.zip -L -o premake_windows.zip + unzip premake_windows.zip + rm premake_windows.zip + elif [[ $OS = "linux" ]]; then + pushd .. + git clone --depth 1 --branch v5.0.0-beta2 https://github.com/premake/premake-core.git + pushd premake-core + if [[ $LINUX_ARCH == "arm64" ]]; then + PREMAKE_MAKE_ARCH=ARM + else + PREMAKE_MAKE_ARCH=x86 + fi + make -f Bootstrap.mak linux PLATFORM=$PREMAKE_MAKE_ARCH + cp bin/release/* ../bin + popd + popd + # curl https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz -L -o premake_linux.tar.gz + # # Export premake5 into bin + # tar -xvf premake_linux.tar.gz 2>/dev/null + # # Delete downloaded archive + # rm premake_linux.tar.gz + fi popd +} +if [[ ! -f "dependencies/bin/premake5" ]]; then + download_premake fi -echo "Using premake5 from $DEPENDENCIES/bin/premake5" -export PREMAKE=$DEPENDENCIES/bin/premake5 -$PREMAKE --version +if [[ ! -d "dependencies/export-compile-commands" ]]; then + pushd dependencies + git clone https://github.com/tarruda/premake-export-compile-commands export-compile-commands + popd +fi -export PREMAKE_PATH="$PWD/../rive-runtime/build":$PREMAKE_PATH +export PREMAKE=$PWD/dependencies/bin/premake5 -OUT=out/lib/$CONFIG -$PREMAKE --scripts=../build --file=./premake5_code_generator.lua --no-rive-decoders --config=$CONFIG --out=$OUT gmake2 +case "$OS" in + macosx|linux) TARGET=gmake2 ;; + windows) TARGET=vs2022; KIND=--shared; EXTRA_OUT=_shared ;; +esac -for var in "$@"; do - if [[ $var = "clean" ]]; then - make -C $OUT clean - fi -done +# export PREMAKE_PATH="$RUNTIME_PATH":$PREMAKE_PATH -make -C $OUT -j$(($(sysctl -n hw.physicalcpu) + 1)) +if [[ $CLEAN == true ]]; then + echo 'Cleaning...' + rm -fR out +fi -for var in "$@"; do - if [[ $var = "run" ]]; then - $OUT/rive_code_generator --help - fi - if [[ $var = "dev" ]]; then - # $OUT/rive_code_generator -i ../samples/signage_v03.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache - # $OUT/rive_code_generator -i ../samples/rating.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache - # $OUT/rive_code_generator -i ../samples/ -o out/generated/rive_generated.dart -t ../templates/dart_template.mustache - $OUT/rive_code_generator -i ../samples/ -o out/generated/rive_viewmodel.dart -t ../templates/viewmodel_template.mustache - # $OUT/rive_code_generator -i ../samples/ -o out/generated/rive.json -t ../templates/json_template.mustache - # $OUT/rive_code_generator -i ../samples/nested_test.riv -o out/rive_generated.dart --help - # $OUT/rive_code_generator -i ../samples/ -o out/rive_generated.dart - fi -done \ No newline at end of file +OUT="out/lib/$CONFIG" +OUT="${OUT%/}" # Remove trailing slash if it exists +$PREMAKE --scripts=../rive-runtime/build/ --file=premake5_code_generator.lua $TARGET --config=$CONFIG --out=$OUT + +if [ "$OS" = "macosx" ]; then + NUM_CORES=$(($(sysctl -n hw.physicalcpu) + 1)) +elif [ "$OS" = "linux" ]; then + NUM_CORES=$(nproc) +elif [ "$OS" = "windows" ]; then + NUM_CORES=$NUMBER_OF_PROCESSORS +else + echo "Unsupported OS: $OS" + exit 1 +fi + +if [[ $OS = "windows" ]]; then + pushd "$OUT" + msbuild.exe rive.sln -m:$NUM_CORES + popd +else + make -C $OUT -j$NUM_CORES +fi + +EXECUTABLE_NAME=rive_code_generator +if [[ $OS = "windows" ]]; then + EXECUTABLE="$EXECUTABLE_NAME.exe" +else + EXECUTABLE="$EXECUTABLE_NAME" +fi + +echo -e "\033[0;32m\nBuild complete: $OUT/$EXECUTABLE\033[0m" + +if [[ $RUN == true ]]; then + "$OUT/rive_code_generator" --help +fi +if [[ $DEV == true ]]; then + pwd + # "$OUT/$EXECUTABLE" -i ../samples/signage_v03.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache + # "$OUT/$EXECUTABLE" -i ../samples/rating.riv -o out/rive_generated.dart -t ../templates/dart_template.mustache + # "$OUT/$EXECUTABLE" -i ../samples/ -o out/generated/rive_generated.dart -t ../templates/dart_template.mustache + "$OUT/$EXECUTABLE" -i ../samples/ -o out/generated/rive_viewmodel.dart -t ../templates/viewmodel_template.mustache + # "$OUT/$EXECUTABLE" -i ../samples/ -o out/generated/rive.json -t ../templates/json_template.mustache + # "$OUT/$EXECUTABLE" -i ../samples/nested_test.riv -o out/rive_generated.dart --help + # "$OUT/$EXECUTABLE" -i ../samples/ -o out/rive_generated.dart +fi \ No newline at end of file diff --git a/build/premake5_code_generator.lua b/build/premake5_code_generator.lua index 5de47fd..ca26c8a 100644 --- a/build/premake5_code_generator.lua +++ b/build/premake5_code_generator.lua @@ -3,7 +3,6 @@ rive = '../rive-runtime' -- dofile(path.join(path.getabsolute(rive) .. '/build', 'premake5.lua')) -- dofile(path.join(path.getabsolute(rive) .. '/build', 'rive_build_config.lua')) dofile(path.join(path.getabsolute(rive) .. '/premake5_v2.lua')) -dofile(path.join(path.getabsolute(rive) .. '/renderer/premake5_pls_renderer.lua')) project('rive_code_generator') do @@ -34,4 +33,8 @@ do -- -- 'CoreText.framework', -- }) -- end + + filter "action:vs*" + buildoptions { "/EHsc" } + filter {} end \ No newline at end of file From 119715c8740ff9b563f26178bae2bfae3b6e2b74 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Sun, 15 Jun 2025 14:39:05 +0200 Subject: [PATCH 5/7] chore: fix build warnings --- src/main.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b33659c..1e2f443 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,7 +121,7 @@ struct RiveFileData }; // Helper function to convert a string to the specified case style -std::string toCaseHelper(const std::string& str, CaseStyle style) +static std::string toCaseHelper(const std::string& str, CaseStyle style) { std::stringstream result; bool capitalizeNext = (style == CaseStyle::Pascal); @@ -186,7 +186,7 @@ std::string toCaseHelper(const std::string& str, CaseStyle style) return finalResult; } -std::string toCamelCase(const std::string& str) +static std::string toCamelCase(const std::string& str) { std::string result = toCaseHelper(str, CaseStyle::Camel); // Handle Dart reserved keywords @@ -202,22 +202,22 @@ std::string toCamelCase(const std::string& str) return result; } -std::string toPascalCase(const std::string& str) +static std::string toPascalCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Pascal); } -std::string toSnakeCase(const std::string& str) +static std::string toSnakeCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Snake); } -std::string toKebabCase(const std::string& str) +static std::string toKebabCase(const std::string& str) { return toCaseHelper(str, CaseStyle::Kebab); } -std::string sanitizeString(const std::string& input) +static std::string sanitizeString(const std::string& input) { std::string output; for (char c : input) @@ -295,7 +295,7 @@ static std::vector get_animations_from_artboard( return animations; } -std::vector>> +static std::vector>> get_state_machines_from_artboard(rive::ArtboardInstance* artboard) { std::vector>> state_machines; @@ -353,7 +353,7 @@ get_state_machines_from_artboard(rive::ArtboardInstance* artboard) return state_machines; } -std::vector find_riv_files(const std::string& path) +static std::vector find_riv_files(const std::string& path) { std::vector riv_files; @@ -375,7 +375,7 @@ std::vector find_riv_files(const std::string& path) return riv_files; } -std::string makeUnique(const std::string& base, +static std::string makeUnique(const std::string& base, std::unordered_set& usedNames) { std::string uniqueName = base; @@ -401,7 +401,7 @@ void findAll(std::vector& results, rive::ArtboardInstance* artboard) } } -std::vector get_text_value_runs_from_artboard( +static std::vector get_text_value_runs_from_artboard( rive::ArtboardInstance* artboard) { std::vector text_value_runs; @@ -420,7 +420,7 @@ std::vector get_text_value_runs_from_artboard( return text_value_runs_info; } -std::vector +static std::vector get_nested_text_value_run_paths_from_artboard( rive::ArtboardInstance* artboard, const std::string& current_path = "") @@ -463,7 +463,7 @@ get_nested_text_value_run_paths_from_artboard( return nested_text_value_runs_info; } -std::vector get_assets_from_file(rive::File* file) +static std::vector get_assets_from_file(rive::File* file) { std::vector assetsInfo; std::unordered_set usedAssetNames; @@ -501,7 +501,7 @@ std::vector get_assets_from_file(rive::File* file) return assetsInfo; } -std::string dataTypeToString(rive::DataType type) +static std::string dataTypeToString(rive::DataType type) { switch (type) { @@ -534,7 +534,7 @@ std::string dataTypeToString(rive::DataType type) } } -std::optional process_riv_file(const std::string& rive_file_path) +static std::optional process_riv_file(const std::string& rive_file_path) { // Check if the file is empty if (std::filesystem::is_empty(rive_file_path)) @@ -668,7 +668,7 @@ std::optional process_riv_file(const std::string& rive_file_path) return file_data; } -std::optional read_template_file(const std::string& path) +static std::optional read_template_file(const std::string& path) { std::ifstream file(path); if (!file.is_open()) @@ -709,7 +709,7 @@ int main(int argc, char* argv[]) {"js", Language::JavaScript}}, CLI::ignore_case)); - CLI11_PARSE(app, argc, argv); + CLI11_PARSE(app, argc, argv) std::string template_str; if (!template_path.empty()) From 744221414263c767c71ad24510643dc7c9b99600 Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Sun, 15 Jun 2025 15:48:45 +0200 Subject: [PATCH 6/7] refactor: prefer camelCase --- src/main.cpp | 684 +++++++++++++++++++++++++-------------------------- 1 file changed, 341 insertions(+), 343 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1e2f443..80d3a75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,7 +26,7 @@ #include "rive/viewmodel/viewmodel_property_enum.hpp" #include "utils/no_op_factory.hpp" -const std::string generated_file_name = "rive_generated"; +const std::string generatedFileName = "rive_generated"; enum class CaseStyle { @@ -46,13 +46,13 @@ struct InputInfo { std::string name; std::string type; - std::string default_value; + std::string defaultValue; }; struct TextValueRunInfo { std::string name; - std::string default_value; + std::string defaultValue; }; struct NestedTextValueRunInfo @@ -65,10 +65,10 @@ struct AssetInfo { std::string name; std::string type; - std::string file_extension; - std::string asset_id; - std::string cdn_uuid; - std::string cdn_base_url; + std::string fileExtension; + std::string assetId; + std::string cdnUuid; + std::string cdnBaseUrl; }; struct EnumValueInfo @@ -86,7 +86,7 @@ struct PropertyInfo { std::string name; std::string type; - std::string backing_name; + std::string backingName; }; struct ViewModelInfo @@ -97,27 +97,27 @@ struct ViewModelInfo struct ArtboardData { - std::string artboard_name; - std::string artboard_pascal_case; - std::string artboard_camel_case; - std::string artboard_snake_case; - std::string artboard_kebab_case; + std::string artboardName; + std::string artboardPascalCase; + std::string artboardCameCase; + std::string artboardSnakeCase; + std::string artboardKebabCase; std::vector animations; - std::vector>> state_machines; - std::vector text_value_runs; - std::vector nested_text_value_runs; + std::vector>> stateMachines; + std::vector textValueRuns; + std::vector nestedTextValueRuns; }; struct RiveFileData { - std::string riv_pascal_case; - std::string riv_camel_case; - std::string riv_snake_case; - std::string riv_kebab_case; + std::string rivPascalCase; + std::string rivCameCase; + std::string riveSnakeCase; + std::string rivKebabCase; std::vector artboards; std::vector assets; std::vector enums; - std::vector view_models; + std::vector viewmodels; }; // Helper function to convert a string to the specified case style @@ -189,6 +189,8 @@ static std::string toCaseHelper(const std::string& str, CaseStyle style) static std::string toCamelCase(const std::string& str) { std::string result = toCaseHelper(str, CaseStyle::Camel); + // TODO: These handlers are generic to dart, we need to make something more + // generic to handle all languages // Handle Dart reserved keywords if (result == "with" || result == "class" || result == "enum" || result == "var" || result == "const" || result == "final" || @@ -258,7 +260,7 @@ static std::string sanitizeString(const std::string& input) return output; } -static std::unique_ptr open_file(const char name[]) +static std::unique_ptr openFile(const char name[]) { FILE* f = fopen(name, "rb"); if (!f) @@ -282,7 +284,7 @@ static std::unique_ptr open_file(const char name[]) return rive::File::import(bytes, &gFactory); } -static std::vector get_animations_from_artboard( +static std::vector getAnimationsFromArtboard( rive::ArtboardInstance* artboard) { std::vector animations; @@ -296,14 +298,14 @@ static std::vector get_animations_from_artboard( } static std::vector>> -get_state_machines_from_artboard(rive::ArtboardInstance* artboard) +getStateMachinesFromArtboard(rive::ArtboardInstance* artboard) { - std::vector>> state_machines; + std::vector>> stateMachines; auto stateMachineCount = artboard->stateMachineCount(); for (int i = 0; i < stateMachineCount; i++) { auto stateMachine = artboard->stateMachineAt(i); - std::string state_machine_name = stateMachine->name(); + std::string stateMachineName = stateMachine->name(); std::vector inputs; auto inputCount = stateMachine->inputCount(); @@ -348,14 +350,14 @@ get_state_machines_from_artboard(rive::ArtboardInstance* artboard) inputs.push_back({input->name(), inputType, defaultValue}); } - state_machines.emplace_back(state_machine_name, inputs); + stateMachines.emplace_back(stateMachineName, inputs); } - return state_machines; + return stateMachines; } -static std::vector find_riv_files(const std::string& path) +static std::vector findRiveFiles(const std::string& path) { - std::vector riv_files; + std::vector riveFile; if (std::filesystem::is_directory(path)) { @@ -363,16 +365,16 @@ static std::vector find_riv_files(const std::string& path) { if (entry.path().extension() == ".riv") { - riv_files.push_back(entry.path().string()); + riveFile.push_back(entry.path().string()); } } } else if (std::filesystem::path(path).extension() == ".riv") { - riv_files.push_back(path); + riveFile.push_back(path); } - return riv_files; + return riveFile; } static std::string makeUnique(const std::string& base, @@ -401,40 +403,40 @@ void findAll(std::vector& results, rive::ArtboardInstance* artboard) } } -static std::vector get_text_value_runs_from_artboard( +static std::vector getTextValueRunsFromArtboard( rive::ArtboardInstance* artboard) { - std::vector text_value_runs; - std::vector text_value_runs_info; + std::vector textValueRuns; + std::vector textValueRunsInfo; - findAll(text_value_runs, artboard); + findAll(textValueRuns, artboard); - for (auto text_value_run : text_value_runs) + for (auto textValueRun : textValueRuns) { - if (!text_value_run->name().empty()) + if (!textValueRun->name().empty()) { - text_value_runs_info.push_back( - {text_value_run->name(), text_value_run->text()}); + textValueRunsInfo.push_back( + {textValueRun->name(), textValueRun->text()}); } } - return text_value_runs_info; + return textValueRunsInfo; } static std::vector -get_nested_text_value_run_paths_from_artboard( +getNestedTextValueRunPathsFromArtboard( rive::ArtboardInstance* artboard, - const std::string& current_path = "") + const std::string& currentPath = "") { - std::vector nested_text_value_runs_info; + std::vector nestedTextValueRunsInfo; auto count = artboard->nestedArtboards().size(); - if (!current_path.empty()) + if (!currentPath.empty()) { - auto text_runs = get_text_value_runs_from_artboard(artboard); - for (const auto& text_run : text_runs) + auto textRuns = getTextValueRunsFromArtboard(artboard); + for (const auto& textRun : textRuns) { - nested_text_value_runs_info.push_back( - {text_run.name, current_path}); + nestedTextValueRunsInfo.push_back( + {textRun.name, currentPath}); } } @@ -442,28 +444,28 @@ get_nested_text_value_run_paths_from_artboard( for (int i = 0; i < count; i++) { auto nested = artboard->nestedArtboards()[i]; - auto nested_name = nested->name(); - if (!nested_name.empty()) + auto nestedName = nested->name(); + if (!nestedName.empty()) { // Only process nested artboards that have an exported name - std::string new_path = current_path.empty() + std::string newPath = currentPath.empty() ? nested->name() - : current_path + "/" + nested->name(); + : currentPath + "/" + nested->name(); - auto nested_results = get_nested_text_value_run_paths_from_artboard( + auto nestedResults = getNestedTextValueRunPathsFromArtboard( nested->artboardInstance(), - new_path); - nested_text_value_runs_info.insert( - nested_text_value_runs_info.end(), - nested_results.begin(), - nested_results.end()); + newPath); + nestedTextValueRunsInfo.insert( + nestedTextValueRunsInfo.end(), + nestedResults.begin(), + nestedResults.end()); } } - return nested_text_value_runs_info; + return nestedTextValueRunsInfo; } -static std::vector get_assets_from_file(rive::File* file) +static std::vector getAssetsFromFile(rive::File* file) { std::vector assetsInfo; std::unordered_set usedAssetNames; @@ -534,33 +536,33 @@ static std::string dataTypeToString(rive::DataType type) } } -static std::optional process_riv_file(const std::string& rive_file_path) +static std::optional processRiveFile(const std::string& riveFilePath) { // Check if the file is empty - if (std::filesystem::is_empty(rive_file_path)) + if (std::filesystem::is_empty(riveFilePath)) { - std::cerr << "Error: Rive file is empty: " << rive_file_path + std::cerr << "Error: Rive file is empty: " << riveFilePath << std::endl; return std::nullopt; } - auto riveFile = open_file(rive_file_path.c_str()); + auto riveFile = openFile(riveFilePath.c_str()); if (!riveFile) { - std::cerr << "Error: Failed to parse Rive file: " << rive_file_path + std::cerr << "Error: Failed to parse Rive file: " << riveFilePath << std::endl; return std::nullopt; } - std::filesystem::path path(rive_file_path); - std::string file_name_without_extension = path.stem().string(); - std::vector assets = get_assets_from_file(riveFile.get()); - RiveFileData file_data; - file_data.riv_pascal_case = toPascalCase(file_name_without_extension); - file_data.riv_camel_case = toCamelCase(file_name_without_extension); - file_data.riv_snake_case = toSnakeCase(file_name_without_extension); - file_data.riv_kebab_case = toKebabCase(file_name_without_extension); - file_data.assets = assets; + std::filesystem::path path(riveFilePath); + std::string fileNameWithoutExtension = path.stem().string(); + std::vector assets = getAssetsFromFile(riveFile.get()); + RiveFileData fileData; + fileData.rivPascalCase = toPascalCase(fileNameWithoutExtension); + fileData.rivCameCase = toCamelCase(fileNameWithoutExtension); + fileData.riveSnakeCase = toSnakeCase(fileNameWithoutExtension); + fileData.rivKebabCase = toKebabCase(fileNameWithoutExtension); + fileData.assets = assets; // Process enums const auto& fileEnums = riveFile->enums(); @@ -575,7 +577,7 @@ static std::optional process_riv_file(const std::string& rive_file { enumInfo.values.push_back({value->key()}); } - file_data.enums.push_back(enumInfo); + fileData.enums.push_back(enumInfo); } } @@ -624,7 +626,7 @@ static std::optional process_riv_file(const std::string& rive_file {property.name, dataTypeToString(property.type)}); } } - file_data.view_models.push_back(viewModelInfo); + fileData.viewmodels.push_back(viewModelInfo); } } @@ -634,41 +636,41 @@ static std::optional process_riv_file(const std::string& rive_file for (int i = 0; i < artboardCount; i++) { auto artboard = riveFile->artboardAt(i); - std::string artboard_name = artboard->name(); + std::string artboardName = artboard->name(); - std::string artboard_pascal_case = toPascalCase(artboard_name); - std::string artboard_camel_case = toCamelCase(artboard_name); - std::string artboard_snake_case = toSnakeCase(artboard_name); - std::string artboard_kebab_case = toKebabCase(artboard_name); + std::string artboardPascalCase = toPascalCase(artboardName); + std::string artboardCameCase = toCamelCase(artboardName); + std::string artboardSnakeCase = toSnakeCase(artboardName); + std::string artboardKebabCase = toKebabCase(artboardName); // Ensure unique artboard variable names - artboard_camel_case = - makeUnique(artboard_camel_case, usedArtboardNames); + artboardCameCase = + makeUnique(artboardCameCase, usedArtboardNames); std::vector animations = - get_animations_from_artboard(artboard.get()); + getAnimationsFromArtboard(artboard.get()); std::vector>> - state_machines = get_state_machines_from_artboard(artboard.get()); - std::vector text_value_runs = - get_text_value_runs_from_artboard(artboard.get()); - std::vector nested_text_value_runs = - get_nested_text_value_run_paths_from_artboard(artboard.get()); - - file_data.artboards.push_back({artboard_name, - artboard_pascal_case, - artboard_camel_case, - artboard_snake_case, - artboard_kebab_case, + stateMachines = getStateMachinesFromArtboard(artboard.get()); + std::vector textValueRuns = + getTextValueRunsFromArtboard(artboard.get()); + std::vector nestedTextValueRuns = + getNestedTextValueRunPathsFromArtboard(artboard.get()); + + fileData.artboards.push_back({artboardName, + artboardPascalCase, + artboardCameCase, + artboardSnakeCase, + artboardKebabCase, animations, - state_machines, - text_value_runs, - nested_text_value_runs}); + stateMachines, + textValueRuns, + nestedTextValueRuns}); } - return file_data; + return fileData; } -static std::optional read_template_file(const std::string& path) +static std::optional readTemplateFile(const std::string& path) { std::ifstream file(path); if (!file.is_open()) @@ -685,21 +687,21 @@ int main(int argc, char* argv[]) { CLI::App app{"Rive Code Generator"}; - std::string input_path; - std::string output_file_path; - std::string template_path; + std::string inputPath; + std::string outputFilePath; + std::string templatePath; Language language = Language::Dart; // Default to Dart app.add_option("-i, --input", - input_path, + inputPath, "Path to Rive file or directory containing Rive files") ->required() ->check(CLI::ExistingFile | CLI::ExistingDirectory); - app.add_option("-o, --output", output_file_path, "Output file path") + app.add_option("-o, --output", outputFilePath, "Output file path") ->required(); - app.add_option("-t,--template", template_path, "Custom template file path"); + app.add_option("-t,--template", templatePath, "Custom template file path"); app.add_option("-l, --language", language, @@ -711,14 +713,14 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv) - std::string template_str; - if (!template_path.empty()) + std::string templateStr; + if (!templatePath.empty()) { - auto custom_template = read_template_file(template_path); - if (custom_template) + auto customTemplate = readTemplateFile(templatePath); + if (customTemplate) { - template_str = *custom_template; - std::cout << "Using custom template from: " << template_path + templateStr = *customTemplate; + std::cout << "Using custom template from: " << templatePath << std::endl; } else @@ -726,14 +728,14 @@ int main(int argc, char* argv[]) // TODO: This is probably not needed. Or can have a safety to // fallback to the language specified std::cout << "Falling back to default template." << std::endl; - template_str = default_templates::DEFAULT_DART_TEMPLATE; + templateStr = default_templates::DEFAULT_DART_TEMPLATE; } } else { if (language == Language::Dart) { - template_str = default_templates::DEFAULT_DART_TEMPLATE; + templateStr = default_templates::DEFAULT_DART_TEMPLATE; } else if (language == Language::JavaScript) { @@ -743,311 +745,307 @@ int main(int argc, char* argv[]) } } - std::vector riv_files = find_riv_files(input_path); + std::vector riveFiles = findRiveFiles(inputPath); - if (riv_files.empty()) + if (riveFiles.empty()) { std::cerr << "No .riv files found in the specified path." << std::endl; return 1; } - std::vector rive_file_data_list; - for (const auto& riv_file : riv_files) + std::vector riveFileDataList; + for (const auto& riv_file : riveFiles) { - auto result = process_riv_file(riv_file); + auto result = processRiveFile(riv_file); if (result) { - rive_file_data_list.push_back(*result); + riveFileDataList.push_back(*result); } // If result is nullopt, the error has already been printed } // Mustache template rendering - kainjow::mustache::data template_data; - std::vector riv_file_list; + kainjow::mustache::data templateData; + std::vector riveFileList; - for (size_t file_index = 0; file_index < rive_file_data_list.size(); - file_index++) + for (size_t fileIndex = 0; fileIndex < riveFileDataList.size(); + fileIndex++) { - const auto& file_data = rive_file_data_list[file_index]; - kainjow::mustache::data riv_file_data; - riv_file_data["riv_pascal_case"] = file_data.riv_pascal_case; - riv_file_data["riv_camel_case"] = file_data.riv_camel_case; - riv_file_data["riv_snake_case"] = file_data.riv_snake_case; - riv_file_data["riv_kebab_case"] = file_data.riv_kebab_case; - riv_file_data["last"] = (file_index == rive_file_data_list.size() - 1); + const auto& fileData = riveFileDataList[fileIndex]; + kainjow::mustache::data riveFileData; + riveFileData["riv_pascal_case"] = fileData.rivPascalCase; + riveFileData["riv_camel_case"] = fileData.rivCameCase; + riveFileData["riv_snake_case"] = fileData.riveSnakeCase; + riveFileData["riv_kebab_case"] = fileData.rivKebabCase; + riveFileData["last"] = (fileIndex == riveFileDataList.size() - 1); // Add enums to template data std::vector enums; - for (size_t enum_index = 0; enum_index < file_data.enums.size(); - enum_index++) + for (size_t enumIndex = 0; enumIndex < fileData.enums.size(); + enumIndex++) { - const auto& enum_info = file_data.enums[enum_index]; - kainjow::mustache::data enum_data; - enum_data["enum_name"] = enum_info.name; - enum_data["enum_camel_case"] = toCamelCase(enum_info.name); - enum_data["enum_pascal_case"] = toPascalCase(enum_info.name); - enum_data["enum_snake_case"] = toSnakeCase(enum_info.name); - enum_data["enum_kebab_case"] = toKebabCase(enum_info.name); - enum_data["last"] = (enum_index == file_data.enums.size() - 1); - - std::vector enum_values; - for (size_t value_index = 0; value_index < enum_info.values.size(); - value_index++) + const auto& enumInfo = fileData.enums[enumIndex]; + kainjow::mustache::data enumData; + enumData["enum_name"] = enumInfo.name; + enumData["enum_camel_case"] = toCamelCase(enumInfo.name); + enumData["enum_pascal_case"] = toPascalCase(enumInfo.name); + enumData["enum_snake_case"] = toSnakeCase(enumInfo.name); + enumData["enum_kebab_case"] = toKebabCase(enumInfo.name); + enumData["last"] = (enumIndex == fileData.enums.size() - 1); + + std::vector enumValues; + for (size_t valueIndex = 0; valueIndex < enumInfo.values.size(); + valueIndex++) { - const auto& value = enum_info.values[value_index]; - kainjow::mustache::data value_data; - value_data["enum_value_key"] = value.key; - value_data["enum_value_camel_case"] = toCamelCase(value.key); - value_data["enum_value_pascal_case"] = toPascalCase(value.key); - value_data["enum_value_snake_case"] = toSnakeCase(value.key); - value_data["enum_value_kebab_case"] = toKebabCase(value.key); - value_data["last"] = - (value_index == enum_info.values.size() - 1); - enum_values.push_back(value_data); + const auto& value = enumInfo.values[valueIndex]; + kainjow::mustache::data valueData; + valueData["enum_value_key"] = value.key; + valueData["enum_value_camel_case"] = toCamelCase(value.key); + valueData["enum_value_pascal_case"] = toPascalCase(value.key); + valueData["enum_value_snake_case"] = toSnakeCase(value.key); + valueData["enum_value_kebab_case"] = toKebabCase(value.key); + valueData["last"] = + (valueIndex == enumInfo.values.size() - 1); + enumValues.push_back(valueData); } - enum_data["enum_values"] = enum_values; - enums.push_back(enum_data); + enumData["enum_values"] = enumValues; + enums.push_back(enumData); } - riv_file_data["enums"] = enums; + riveFileData["enums"] = enums; // Add view models to template data - std::vector view_models; - for (size_t vm_index = 0; vm_index < file_data.view_models.size(); - vm_index++) + std::vector viewmodels; + for (size_t vmIndex = 0; vmIndex < fileData.viewmodels.size(); + vmIndex++) { - const auto& view_model = file_data.view_models[vm_index]; - kainjow::mustache::data view_model_data; - view_model_data["view_model_name"] = view_model.name; - view_model_data["view_model_camel_case"] = - toCamelCase(view_model.name); - view_model_data["view_model_pascal_case"] = - toPascalCase(view_model.name); - view_model_data["view_model_snake_case"] = - toSnakeCase(view_model.name); - view_model_data["view_model_kebab_case"] = - toKebabCase(view_model.name); - view_model_data["last"] = - (vm_index == file_data.view_models.size() - 1); + const auto& viewModel = fileData.viewmodels[vmIndex]; + kainjow::mustache::data viewmodelData; + viewmodelData["view_model_name"] = viewModel.name; + viewmodelData["view_model_camel_case"] = + toCamelCase(viewModel.name); + viewmodelData["view_model_pascal_case"] = + toPascalCase(viewModel.name); + viewmodelData["view_model_snake_case"] = + toSnakeCase(viewModel.name); + viewmodelData["view_model_kebab_case"] = + toKebabCase(viewModel.name); + viewmodelData["last"] = + (vmIndex == fileData.viewmodels.size() - 1); std::vector properties; - for (size_t prop_index = 0; - prop_index < view_model.properties.size(); - prop_index++) + for (size_t propIndex = 0; + propIndex < viewModel.properties.size(); + propIndex++) { - const auto& property = view_model.properties[prop_index]; - kainjow::mustache::data property_data; - property_data["property_name"] = property.name; - property_data["property_camel_case"] = + const auto& property = viewModel.properties[propIndex]; + kainjow::mustache::data propertyData; + propertyData["property_name"] = property.name; + propertyData["property_camel_case"] = toCamelCase(property.name); - property_data["property_pascal_case"] = + propertyData["property_pascal_case"] = toPascalCase(property.name); - property_data["property_snake_case"] = + propertyData["property_snake_case"] = toSnakeCase(property.name); - property_data["property_kebab_case"] = + propertyData["property_kebab_case"] = toKebabCase(property.name); - property_data["property_type"] = property.type; + propertyData["property_type"] = property.type; // Add property type information for the viewmodel template - kainjow::mustache::data property_type_data; - property_type_data.set("is_view_model", + kainjow::mustache::data propertyTypeData; + propertyTypeData.set("is_view_model", property.type == "viewModel"); - property_type_data.set("is_enum", property.type == "enum"); - property_type_data.set("is_string", property.type == "string"); - property_type_data.set("is_number", property.type == "number"); - property_type_data.set("is_integer", + propertyTypeData.set("is_enum", property.type == "enum"); + propertyTypeData.set("is_string", property.type == "string"); + propertyTypeData.set("is_number", property.type == "number"); + propertyTypeData.set("is_integer", property.type == "integer"); - property_type_data.set("is_boolean", + propertyTypeData.set("is_boolean", property.type == "boolean"); - property_type_data.set("is_color", property.type == "color"); - property_type_data.set("is_list", property.type == "list"); - property_type_data.set("is_trigger", + propertyTypeData.set("is_color", property.type == "color"); + propertyTypeData.set("is_list", property.type == "list"); + propertyTypeData.set("is_trigger", property.type == "trigger"); - property_type_data.set("backing_name", property.backing_name); - property_type_data.set("backing_camel_case", - toCamelCase(property.backing_name)); - property_type_data.set("backing_pascal_case", - toPascalCase(property.backing_name)); - property_type_data.set("backing_snake_case", - toSnakeCase(property.backing_name)); - property_type_data.set("backing_kebab_case", - toKebabCase(property.backing_name)); - property_data.set("property_type", property_type_data); - - property_data["last"] = - (prop_index == view_model.properties.size() - 1); - properties.push_back(property_data); + propertyTypeData.set("backing_name", property.backingName); + propertyTypeData.set("backing_camel_case", + toCamelCase(property.backingName)); + propertyTypeData.set("backing_pascal_case", + toPascalCase(property.backingName)); + propertyTypeData.set("backing_snake_case", + toSnakeCase(property.backingName)); + propertyTypeData.set("backing_kebab_case", + toKebabCase(property.backingName)); + propertyData.set("property_type", propertyTypeData); + + propertyData["last"] = + (propIndex == viewModel.properties.size() - 1); + properties.push_back(propertyData); } - view_model_data["properties"] = properties; - view_models.push_back(view_model_data); + viewmodelData["properties"] = properties; + viewmodels.push_back(viewmodelData); } - riv_file_data["view_models"] = view_models; + riveFileData["view_models"] = viewmodels; std::vector assets; - for (size_t asset_index = 0; asset_index < file_data.assets.size(); - asset_index++) + for (size_t assetIndex = 0; assetIndex < fileData.assets.size(); + assetIndex++) { - const auto& asset = file_data.assets[asset_index]; - kainjow::mustache::data asset_data; - asset_data["asset_name"] = asset.name; - asset_data["asset_camel_case"] = toCamelCase(asset.name); - asset_data["asset_pascal_case"] = toPascalCase(asset.name); - asset_data["asset_snake_case"] = toSnakeCase(asset.name); - asset_data["asset_kebab_case"] = toKebabCase(asset.name); - asset_data["asset_type"] = asset.type; - asset_data["asset_id"] = asset.asset_id; - asset_data["asset_cdn_uuid"] = asset.cdn_uuid; - asset_data["asset_cdn_base_url"] = asset.cdn_base_url; - asset_data["last"] = (asset_index == file_data.assets.size() - 1); - assets.push_back(asset_data); + const auto& asset = fileData.assets[assetIndex]; + kainjow::mustache::data assetData; + assetData["asset_name"] = asset.name; + assetData["asset_camel_case"] = toCamelCase(asset.name); + assetData["asset_pascal_case"] = toPascalCase(asset.name); + assetData["asset_snake_case"] = toSnakeCase(asset.name); + assetData["asset_kebab_case"] = toKebabCase(asset.name); + assetData["asset_type"] = asset.type; + assetData["asset_id"] = asset.assetId; + assetData["asset_cdn_uuid"] = asset.cdnUuid; + assetData["asset_cdn_base_url"] = asset.cdnBaseUrl; + assetData["last"] = (assetIndex == fileData.assets.size() - 1); + assets.push_back(assetData); } - riv_file_data["assets"] = assets; + riveFileData["assets"] = assets; - std::vector artboard_list; - for (size_t artboard_index = 0; - artboard_index < file_data.artboards.size(); - artboard_index++) + std::vector artboardList; + for (size_t artboardIndex = 0; + artboardIndex < fileData.artboards.size(); + artboardIndex++) { - const auto& artboard = file_data.artboards[artboard_index]; - kainjow::mustache::data artboard_data; - artboard_data["artboard_name"] = artboard.artboard_name; - artboard_data["artboard_pascal_case"] = - artboard.artboard_pascal_case; - artboard_data["artboard_camel_case"] = artboard.artboard_camel_case; - artboard_data["artboard_snake_case"] = artboard.artboard_snake_case; - artboard_data["artboard_kebab_case"] = artboard.artboard_kebab_case; - artboard_data["last"] = - (artboard_index == file_data.artboards.size() - 1); + const auto& artboard = fileData.artboards[artboardIndex]; + kainjow::mustache::data artboardData; + artboardData["artboard_name"] = artboard.artboardName; + artboardData["artboard_pascal_case"] = + artboard.artboardPascalCase; + artboardData["artboard_camel_case"] = artboard.artboardCameCase; + artboardData["artboard_snake_case"] = artboard.artboardSnakeCase; + artboardData["artboard_kebab_case"] = artboard.artboardKebabCase; + artboardData["last"] = + (artboardIndex == fileData.artboards.size() - 1); std::unordered_set usedAnimationNames; std::vector animations; - for (size_t anim_index = 0; anim_index < artboard.animations.size(); - anim_index++) + for (size_t animIndex = 0; animIndex < artboard.animations.size(); + animIndex++) { - const auto& animation = artboard.animations[anim_index]; - kainjow::mustache::data anim_data; - auto unique_name = makeUnique(animation, usedAnimationNames); - anim_data["animation_name"] = animation; - anim_data["animation_camel_case"] = toCamelCase(unique_name); - anim_data["animation_pascal_case"] = toPascalCase(unique_name); - anim_data["animation_snake_case"] = toSnakeCase(unique_name); - anim_data["animation_kebab_case"] = toKebabCase(unique_name); - anim_data["last"] = - (anim_index == artboard.animations.size() - 1); - animations.push_back(anim_data); + const auto& animation = artboard.animations[animIndex]; + kainjow::mustache::data animData; + auto uniqueName = makeUnique(animation, usedAnimationNames); + animData["animation_name"] = animation; + animData["animation_camel_case"] = toCamelCase(uniqueName); + animData["animation_pascal_case"] = toPascalCase(uniqueName); + animData["animation_snake_case"] = toSnakeCase(uniqueName); + animData["animation_kebab_case"] = toKebabCase(uniqueName); + animData["last"] = + (animIndex == artboard.animations.size() - 1); + animations.push_back(animData); } - artboard_data["animations"] = animations; + artboardData["animations"] = animations; std::unordered_set usedStateMachineNames; - std::vector state_machines; - for (size_t sm_index = 0; sm_index < artboard.state_machines.size(); - sm_index++) + std::vector stateMachines; + for (size_t smIndex = 0; smIndex < artboard.stateMachines.size(); + smIndex++) { - const auto& state_machine = artboard.state_machines[sm_index]; - kainjow::mustache::data state_machine_data; - auto unique_name = - makeUnique(state_machine.first, usedStateMachineNames); - state_machine_data["state_machine_name"] = state_machine.first; - state_machine_data["state_machine_camel_case"] = - toCamelCase(unique_name); - state_machine_data["state_machine_pascal_case"] = - toPascalCase(unique_name); - state_machine_data["state_machine_snake_case"] = - toSnakeCase(unique_name); - state_machine_data["state_machine_kebab_case"] = - toKebabCase(unique_name); - state_machine_data["last"] = - (sm_index == artboard.state_machines.size() - 1); + const auto& stateMachine = artboard.stateMachines[smIndex]; + kainjow::mustache::data stateMachineData; + auto uniqueName = + makeUnique(stateMachine.first, usedStateMachineNames); + stateMachineData["state_machine_name"] = stateMachine.first; + stateMachineData["state_machine_camel_case"] = + toCamelCase(uniqueName); + stateMachineData["state_machine_pascal_case"] = + toPascalCase(uniqueName); + stateMachineData["state_machine_snake_case"] = + toSnakeCase(uniqueName); + stateMachineData["state_machine_kebab_case"] = + toKebabCase(uniqueName); + stateMachineData["last"] = + (smIndex == artboard.stateMachines.size() - 1); std::unordered_set usedInputNames; std::vector inputs; - for (size_t input_index = 0; - input_index < state_machine.second.size(); - input_index++) + for (size_t inputIndex = 0; + inputIndex < stateMachine.second.size(); + inputIndex++) { - const auto& input = state_machine.second[input_index]; - kainjow::mustache::data input_data; - auto unique_name = makeUnique(input.name, usedInputNames); - input_data["input_name"] = input.name; - input_data["input_camel_case"] = toCamelCase(unique_name); - input_data["input_pascal_case"] = toPascalCase(unique_name); - input_data["input_snake_case"] = toSnakeCase(unique_name); - input_data["input_kebab_case"] = toKebabCase(unique_name); - input_data["input_type"] = input.type; - input_data["input_default_value"] = input.default_value; - input_data["last"] = - (input_index == state_machine.second.size() - 1); - inputs.push_back(input_data); + const auto& input = stateMachine.second[inputIndex]; + kainjow::mustache::data inputData; + auto uniqueName = makeUnique(input.name, usedInputNames); + inputData["input_name"] = input.name; + inputData["input_camel_case"] = toCamelCase(uniqueName); + inputData["input_pascal_case"] = toPascalCase(uniqueName); + inputData["input_snake_case"] = toSnakeCase(uniqueName); + inputData["input_kebab_case"] = toKebabCase(uniqueName); + inputData["input_type"] = input.type; + inputData["input_default_value"] = input.defaultValue; + inputData["last"] = + (inputIndex == stateMachine.second.size() - 1); + inputs.push_back(inputData); } - state_machine_data["inputs"] = inputs; + stateMachineData["inputs"] = inputs; - state_machines.push_back(state_machine_data); + stateMachines.push_back(stateMachineData); } - artboard_data["state_machines"] = state_machines; + artboardData["state_machines"] = stateMachines; std::unordered_set usedTextValueRunNames; - std::vector text_value_runs; - for (size_t tvr_index = 0; - tvr_index < artboard.text_value_runs.size(); - tvr_index++) + std::vector textValueRuns; + for (size_t tvrIndex = 0; + tvrIndex < artboard.textValueRuns.size(); + tvrIndex++) { - const auto& tvr = artboard.text_value_runs[tvr_index]; - kainjow::mustache::data tvr_data; - auto unique_name = makeUnique(tvr.name, usedTextValueRunNames); - tvr_data["text_value_run_name"] = tvr.name; - tvr_data["text_value_run_camel_case"] = - toCamelCase(unique_name); - tvr_data["text_value_run_pascal_case"] = - toPascalCase(unique_name); - tvr_data["text_value_run_snake_case"] = - toSnakeCase(unique_name); - tvr_data["text_value_run_kebab_case"] = - toKebabCase(unique_name); - tvr_data["text_value_run_default"] = tvr.default_value; - tvr_data["text_value_run_default_sanitized"] = - sanitizeString(tvr.default_value); - tvr_data["last"] = - (tvr_index == artboard.text_value_runs.size() - 1); - text_value_runs.push_back(tvr_data); + const auto& tvr = artboard.textValueRuns[tvrIndex]; + kainjow::mustache::data tvrData; + auto uniqueName = makeUnique(tvr.name, usedTextValueRunNames); + tvrData["text_value_run_name"] = tvr.name; + tvrData["text_value_run_camel_case"] = + toCamelCase(uniqueName); + tvrData["text_value_run_pascal_case"] = + toPascalCase(uniqueName); + tvrData["text_value_run_snake_case"] = + toSnakeCase(uniqueName); + tvrData["text_value_run_kebab_case"] = + toKebabCase(uniqueName); + tvrData["text_value_run_default"] = tvr.defaultValue; + tvrData["text_value_run_default_sanitized"] = + sanitizeString(tvr.defaultValue); + tvrData["last"] = + (tvrIndex == artboard.textValueRuns.size() - 1); + textValueRuns.push_back(tvrData); } - artboard_data["text_value_runs"] = text_value_runs; + artboardData["text_value_runs"] = textValueRuns; - std::vector nested_text_value_runs; - for (size_t ntvr_index = 0; - ntvr_index < artboard.nested_text_value_runs.size(); - ntvr_index++) + std::vector nestedTextValueRuns; + for (size_t ntvrIndex = 0; + ntvrIndex < artboard.nestedTextValueRuns.size(); + ntvrIndex++) { - const auto& ntvr = artboard.nested_text_value_runs[ntvr_index]; - kainjow::mustache::data ntvr_data; - ntvr_data["nested_text_value_run_name"] = ntvr.name; - ntvr_data["nested_text_value_run_path"] = ntvr.path; - ntvr_data["last"] = - (ntvr_index == artboard.nested_text_value_runs.size() - 1); - nested_text_value_runs.push_back(ntvr_data); + const auto& ntvr = artboard.nestedTextValueRuns[ntvrIndex]; + kainjow::mustache::data ntvrData; + ntvrData["nested_text_value_run_name"] = ntvr.name; + ntvrData["nested_text_value_run_path"] = ntvr.path; + ntvrData["last"] = + (ntvrIndex == artboard.nestedTextValueRuns.size() - 1); + nestedTextValueRuns.push_back(ntvrData); } - artboard_data["nested_text_value_runs"] = nested_text_value_runs; + artboardData["nested_text_value_runs"] = nestedTextValueRuns; - artboard_list.push_back(artboard_data); + artboardList.push_back(artboardData); } - riv_file_data["artboards"] = artboard_list; - // Only add the section if there are view models or enums - if (!view_models.empty() || !enums.empty()) - { - riv_file_list.push_back(riv_file_data); - } + riveFileData["artboards"] = artboardList; + riveFileList.push_back(riveFileData); } - template_data["generated_file_name"] = generated_file_name; - template_data["riv_files"] = riv_file_list; + templateData["generated_file_name"] = generatedFileName; + templateData["riv_files"] = riveFileList; - kainjow::mustache::mustache tmpl(template_str); - std::string result = tmpl.render(template_data); + kainjow::mustache::mustache tmpl(templateStr); + std::string result = tmpl.render(templateData); - std::cout << "Rive: output_file_path = " << output_file_path << std::endl; + std::cout << "Rive: output_file_path = " << outputFilePath << std::endl; - std::filesystem::path output_path(output_file_path); + std::filesystem::path output_path(outputFilePath); // If only a filename is provided, use the current directory if (output_path.is_relative() && output_path.parent_path().empty()) From 0ca10eba281a00368b1b75895598037a44ebc0ca Mon Sep 17 00:00:00 2001 From: Lance Date: Wed, 29 Oct 2025 11:52:42 -0700 Subject: [PATCH 7/7] docs: update paths to rive files --- .gitignore | 1 + README.md | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f31139a..f7f240c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/dependencies # examples examples/generated/ +output/ # test test/output/ diff --git a/README.md b/README.md index dcdae6f..5f65ad8 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ See the [release workflow](.github/workflows/release_workflow.yaml) for release The latest release can be downloaded from the [Releases](https://github.com/rive-app/rive-code-generator-wip/releases) page. +## Setup + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed setup instructions. + ## Usage Run the code generator using: @@ -64,7 +68,7 @@ Run the code generator using: Example: ```sh -./build/out/lib/release/rive_code_generator -i ./examples/rive_files/animation.riv -o ./examples/generated_code.dart -l dart +./build/out/lib/debug/rive_code_generator -i ./samples/rewards.riv -o ./output/generated_code.dart -l dart ``` ## Custom Templates @@ -72,7 +76,7 @@ Example: You can use custom Mustache templates for code generation: ```sh -./build/out/lib/release/rive_code_generator -i ./rive_files/ -o ./output/rive.json -t templates/json_template.mustache +./build/out/lib/debug/rive_code_generator -i ./samples/rewards.riv -o ./output/rive.dart -t templates/viewmodel_template.mustache ``` Sample templates are available in the [`templates`](./templates) directory.