From a6cb0a35021e82b77c30d62ed32671f4d78d2297 Mon Sep 17 00:00:00 2001 From: amizing25 Date: Tue, 30 Dec 2025 08:05:15 +0700 Subject: [PATCH] Initial Commit --- .gitignore | 1 + .vscode/settings.json | 5 ++ Cargo.lock | 154 +++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++ LICENSE | 21 +++++ README.md | 1 + src/generator/json_registry.rs | 114 ++++++++++++++++++++++++ src/generator/mod.rs | 36 ++++++++ src/main.rs | 17 ++++ 9 files changed, 358 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/generator/json_registry.rs create mode 100644 src/generator/mod.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a906b54 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "editor.tabSize": 4, + "rust-analyzer.check.command": "clippy", +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2ab0ff4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,154 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "proc-macro2" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror", +] + +[[package]] +name = "protoc-gen-jsonregistry" +version = "0.1.0" +dependencies = [ + "protobuf", + "serde", + "serde_json", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "zmij" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4a4e8e9dc5c62d159f04fcdbe07f4c3fb710415aab4754bf11505501e3251d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..141a52f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "protoc-gen-jsonregistry" +version = "0.1.0" +edition = "2024" + +[dependencies] +protobuf = "3.7.2" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.148" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bc10788 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 amizing25 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1298493 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +generate the json registry used for [DynamicProtobuf](https://github.com/ex-RushiaLover/DynamicProtobuf.Runtime) \ No newline at end of file diff --git a/src/generator/json_registry.rs b/src/generator/json_registry.rs new file mode 100644 index 0000000..52dd573 --- /dev/null +++ b/src/generator/json_registry.rs @@ -0,0 +1,114 @@ +use protobuf::{ + Message, UnknownValueRef, descriptor::DescriptorProto, descriptor::field_descriptor_proto::Type, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +const COMMAND_ID_FIELD_NUMBER: u32 = 50_000; +const XOR_CONST_FIELD_NUMBER: u32 = 50_001; + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct ProtoJsonRegistry { + pub messages: HashMap>, + pub command_ids: HashMap, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct MessageField { + pub field_number: u32, + pub wire_type: WireType, + pub xor_const: Option, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub enum WireType { + #[default] + Varint = 0, + Fixed64 = 1, + LengthDelimited = 2, + StartGroup = 3, + EndGroup = 4, + Fixed32 = 5, +} + +impl From for WireType { + fn from(value: Type) -> Self { + match value { + Type::TYPE_INT32 + | Type::TYPE_INT64 + | Type::TYPE_UINT32 + | Type::TYPE_UINT64 + | Type::TYPE_SINT32 + | Type::TYPE_SINT64 + | Type::TYPE_ENUM + | Type::TYPE_BOOL => Self::Varint, + Type::TYPE_FLOAT | Type::TYPE_SFIXED32 | Type::TYPE_FIXED32 => Self::Fixed32, + Type::TYPE_DOUBLE | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => Self::Fixed64, + Type::TYPE_GROUP => Self::StartGroup, + Type::TYPE_STRING | Type::TYPE_MESSAGE | Type::TYPE_BYTES => Self::LengthDelimited, + } + } +} + +/// Generate json registry from given messages +pub fn generate_json_registry( + messages: &[DescriptorProto], + package: &str, + parent_path: Option<&str>, + out: &mut ProtoJsonRegistry, +) { + for message in messages { + if message.options.map_entry() { + continue; + } + + let local_name = message.name(); + + let scoped_name = match parent_path { + Some(parent) => format!("{parent}.{local_name}"), + None => local_name.to_string(), + }; + + let full_name = if package.is_empty() { + scoped_name.clone() + } else { + format!("{package}.{scoped_name}") + }; + + // iterate nested types + if !message.nested_type.is_empty() { + generate_json_registry(&message.nested_type, package, Some(&scoped_name), out); + } + + // collect xor constants + for field in &message.field { + let xor_const_field = if let Some(xor_const_field) = + field.options.unknown_fields().get(XOR_CONST_FIELD_NUMBER) + && let UnknownValueRef::Varint(varint) = xor_const_field + { + Some(varint as u32) + } else { + None + }; + + out.messages.entry(full_name.clone()).or_default().insert( + field.name().to_string(), + MessageField { + field_number: field.number() as u32, + wire_type: field.type_().into(), + xor_const: xor_const_field, + }, + ); + } + + // collect command ids + if let Some(command_id_field) = message + .options + .unknown_fields() + .get(COMMAND_ID_FIELD_NUMBER) + && let UnknownValueRef::Varint(varint) = command_id_field + { + out.command_ids.insert(full_name, varint as u16); + }; + } +} diff --git a/src/generator/mod.rs b/src/generator/mod.rs new file mode 100644 index 0000000..0e4a109 --- /dev/null +++ b/src/generator/mod.rs @@ -0,0 +1,36 @@ +use protobuf::{ + Message, + plugin::{ + CodeGeneratorRequest, CodeGeneratorResponse, + code_generator_response::{Feature, File}, + }, +}; +use std::io; + +use crate::generator::json_registry::{ProtoJsonRegistry, generate_json_registry}; + +mod json_registry; + +pub fn generate(input: &[u8]) -> io::Result { + let codegen_request = CodeGeneratorRequest::parse_from_bytes(input)?; + let mut codegen_response = CodeGeneratorResponse::new(); + + codegen_response.set_supported_features(Feature::FEATURE_PROTO3_OPTIONAL as u64); + + for proto in codegen_request.proto_file { + // skips protobuf import + if proto.name().starts_with("google/protobuf") { + continue; + } + + let mut out = ProtoJsonRegistry::default(); + generate_json_registry(&proto.message_type, proto.package(), None, &mut out); + + let mut json_file = File::new(); + json_file.set_content(serde_json::to_string_pretty(&out).unwrap()); + json_file.set_name(format!("{}.registry.json", proto.name())); + codegen_response.file.push(json_file); + } + + Ok(codegen_response) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9219954 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +use protobuf::Message; +use std::io::{self, Read as _, Write}; + +mod generator; + +fn main() -> io::Result<()> { + let mut buf = Vec::new(); + io::stdin().read_to_end(&mut buf)?; + + let result = generator::generate(&buf)?; + + buf.clear(); + result.write_to_vec(&mut buf)?; + io::stdout().write_all(&buf)?; + + Ok(()) +}