mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-18 22:26:12 +01:00
api/lua: Add auto-generated protobuf definitions
This commit is contained in:
parent
b57c8e0dac
commit
ade04b3833
8 changed files with 1153 additions and 828 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -2255,6 +2255,17 @@ dependencies = [
|
|||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lua-build"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
|||
"api/rust",
|
||||
"api/rust/pinnacle-api-macros",
|
||||
"wlcs_pinnacle",
|
||||
"api/lua/build"
|
||||
]
|
||||
exclude = ["snowcap"]
|
||||
|
||||
|
|
13
api/lua/build/Cargo.toml
Normal file
13
api/lua/build/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "lua-build"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
prost = "0.12.6"
|
||||
prost-types = "0.12.6"
|
||||
indexmap = "2.2.6"
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = "0.12.6"
|
||||
walkdir = "2.5.0"
|
23
api/lua/build/build.rs
Normal file
23
api/lua/build/build.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../../protocol");
|
||||
|
||||
let mut proto_paths = Vec::new();
|
||||
|
||||
for entry in walkdir::WalkDir::new("../../protocol") {
|
||||
let entry = entry.unwrap();
|
||||
|
||||
if entry.file_type().is_file() && entry.path().extension().is_some_and(|ext| ext == "proto")
|
||||
{
|
||||
proto_paths.push(entry.into_path());
|
||||
}
|
||||
}
|
||||
|
||||
let descriptor_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("lua-build.bin");
|
||||
|
||||
prost_build::Config::new()
|
||||
.file_descriptor_set_path(descriptor_path)
|
||||
.compile_protos(&proto_paths, &["../../protocol"])
|
||||
.unwrap();
|
||||
}
|
339
api/lua/build/src/main.rs
Normal file
339
api/lua/build/src/main.rs
Normal file
|
@ -0,0 +1,339 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use prost::Message as _;
|
||||
use prost_types::{
|
||||
field_descriptor_proto::{Label, Type},
|
||||
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, ServiceDescriptorProto,
|
||||
};
|
||||
|
||||
type EnumMap = IndexMap<String, EnumData>;
|
||||
type MessageMap = IndexMap<String, MessageData>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MessageData {
|
||||
fields: Vec<Field>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Field {
|
||||
name: String,
|
||||
label: Option<Label>,
|
||||
r#type: FieldType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FieldType {
|
||||
Builtin(Type),
|
||||
Message(String),
|
||||
Enum(String),
|
||||
}
|
||||
|
||||
fn parse_message_enums(enums: &mut EnumMap, prefix: &str, message: &DescriptorProto) {
|
||||
let prefix = format!("{prefix}.{}", message.name());
|
||||
for r#enum in message.enum_type.iter() {
|
||||
parse_enum(enums, &prefix, r#enum);
|
||||
}
|
||||
|
||||
for msg in message.nested_type.iter() {
|
||||
let name = msg.name();
|
||||
parse_message_enums(enums, &format!("{prefix}.{name}"), msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_enum(enums: &mut EnumMap, prefix: &str, enum_desc: &EnumDescriptorProto) {
|
||||
let name = enum_desc.name().to_string();
|
||||
|
||||
let values = enum_desc.value.iter().map(|val| {
|
||||
let name = val.name().to_string();
|
||||
let number = val.number.unwrap();
|
||||
|
||||
EnumValue { name, number }
|
||||
});
|
||||
|
||||
enums.insert(
|
||||
format!("{prefix}.{name}"),
|
||||
EnumData {
|
||||
values: values.collect(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_message(msgs: &mut MessageMap, prefix: &str, message: &DescriptorProto) {
|
||||
let name = format!("{prefix}.{}", message.name());
|
||||
|
||||
let mut fields: HashMap<Option<i32>, Vec<Field>> = HashMap::new();
|
||||
|
||||
for field in message.field.iter() {
|
||||
fields
|
||||
// .entry(field.oneof_index)
|
||||
.entry(None)
|
||||
.or_default()
|
||||
.push(parse_field(field));
|
||||
}
|
||||
|
||||
let data = MessageData {
|
||||
fields: fields.remove(&None).unwrap_or_default(),
|
||||
};
|
||||
|
||||
msgs.insert(name.clone(), data);
|
||||
|
||||
for msg in message.nested_type.iter() {
|
||||
parse_message(msgs, &name, msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_field(field: &FieldDescriptorProto) -> Field {
|
||||
Field {
|
||||
name: field.name().to_string(),
|
||||
label: field.label.is_some().then_some(field.label()),
|
||||
r#type: {
|
||||
if let Some(type_name) = field.type_name.as_ref() {
|
||||
if let Some(r#type) = field.r#type.is_some().then_some(field.r#type()) {
|
||||
match r#type {
|
||||
Type::Enum => FieldType::Enum(type_name.clone()),
|
||||
Type::Message => FieldType::Message(type_name.clone()),
|
||||
_ => panic!(),
|
||||
}
|
||||
} else {
|
||||
FieldType::Builtin(field.r#type())
|
||||
}
|
||||
} else {
|
||||
FieldType::Builtin(field.r#type())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EnumData {
|
||||
values: Vec<EnumValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EnumValue {
|
||||
name: String,
|
||||
number: i32,
|
||||
}
|
||||
|
||||
fn generate_enum_definitions(enums: &EnumMap) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
for (name, data) in enums.iter() {
|
||||
let mut table = format!("---@enum {name}\nlocal {} = {{\n", name.replace('.', "_"));
|
||||
|
||||
for val in data.values.iter() {
|
||||
table += &format!(" {} = {},\n", &val.name, val.number);
|
||||
}
|
||||
|
||||
table += "}\n\n";
|
||||
|
||||
ret += &table;
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn generate_message_classes(msgs: &MessageMap) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
for (name, data) in msgs.iter() {
|
||||
let mut class = format!("---@class {name}\n");
|
||||
|
||||
for field in data.fields.iter() {
|
||||
let r#type = match &field.r#type {
|
||||
FieldType::Builtin(builtin) => match builtin {
|
||||
Type::Double | Type::Float => "number",
|
||||
Type::Int32
|
||||
| Type::Int64
|
||||
| Type::Uint32
|
||||
| Type::Uint64
|
||||
| Type::Fixed64
|
||||
| Type::Fixed32
|
||||
| Type::Sfixed32
|
||||
| Type::Sfixed64
|
||||
| Type::Sint32
|
||||
| Type::Sint64 => "integer",
|
||||
Type::Bool => "boolean",
|
||||
Type::String | Type::Bytes => "string",
|
||||
Type::Group | Type::Message | Type::Enum => "any",
|
||||
}
|
||||
.to_string(),
|
||||
FieldType::Message(s) | FieldType::Enum(s) => s.trim_start_matches('.').to_string(),
|
||||
};
|
||||
|
||||
let non_nil = if field
|
||||
.label
|
||||
.is_some_and(|label| matches!(label, Label::Required))
|
||||
{
|
||||
""
|
||||
} else {
|
||||
"?"
|
||||
};
|
||||
|
||||
let repeated = if field
|
||||
.label
|
||||
.is_some_and(|label| matches!(label, Label::Repeated))
|
||||
{
|
||||
"[]"
|
||||
} else {
|
||||
"?"
|
||||
};
|
||||
|
||||
class += &format!("---@field {} {type}{repeated}{non_nil}\n", &field.name);
|
||||
}
|
||||
|
||||
class += "\n";
|
||||
|
||||
ret += &class;
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
struct Visited {
|
||||
children: HashMap<String, Visited>,
|
||||
}
|
||||
|
||||
fn generate_message_tables(msgs: &MessageMap) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
let mut visited = HashMap::<String, Visited>::new();
|
||||
|
||||
for name in msgs.keys() {
|
||||
let segments = name.trim_start_matches('.').split('.');
|
||||
let mut current = &mut visited;
|
||||
|
||||
let mut prev_segments = Vec::new();
|
||||
|
||||
for segment in segments {
|
||||
current = &mut current
|
||||
.entry(segment.to_string())
|
||||
.or_insert_with(|| {
|
||||
if prev_segments.is_empty() {
|
||||
ret += &format!("local {segment} = {{}}\n")
|
||||
} else {
|
||||
ret += &format!(
|
||||
"{} = {{}}\n",
|
||||
prev_segments
|
||||
.iter()
|
||||
.chain([&segment])
|
||||
.copied()
|
||||
.collect::<Vec<_>>()
|
||||
.join(".")
|
||||
);
|
||||
}
|
||||
Visited {
|
||||
children: HashMap::new(),
|
||||
}
|
||||
})
|
||||
.children;
|
||||
|
||||
prev_segments.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn populate_table_enums(enums: &EnumMap) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
for name in enums.keys() {
|
||||
let name = name.trim_start_matches('.');
|
||||
let type_name = name.replace('.', "_");
|
||||
|
||||
ret += &format!("{name} = {type_name}\n");
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn populate_service_defs(prefix: &str, service: &ServiceDescriptorProto) -> String {
|
||||
let mut ret = String::new();
|
||||
|
||||
let name = format!("{prefix}.{}", service.name());
|
||||
|
||||
ret += &format!("{name} = {{}}\n");
|
||||
|
||||
for method in service.method.iter() {
|
||||
ret += &format!("{name}.{} = {{}}\n", method.name());
|
||||
ret += &format!("{name}.{}.service = \"{name}\"\n", method.name());
|
||||
ret += &format!("{name}.{n}.method = \"{n}\"\n", n = method.name());
|
||||
ret += &format!(
|
||||
"{name}.{}.request = \"{}\"\n",
|
||||
method.name(),
|
||||
method.input_type()
|
||||
);
|
||||
ret += &format!(
|
||||
"{name}.{}.response = \"{}\"\n",
|
||||
method.name(),
|
||||
method.output_type()
|
||||
);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn generate_returned_table(msgs: &MessageMap) -> String {
|
||||
let mut toplevel_packages = IndexSet::new();
|
||||
|
||||
for name in msgs.keys() {
|
||||
let toplevel_package = name.trim_start_matches('.').split('.').next();
|
||||
if let Some(toplevel_package) = toplevel_package {
|
||||
toplevel_packages.insert(toplevel_package.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let mut ret = String::from("return {\n");
|
||||
|
||||
for pkg in toplevel_packages {
|
||||
ret += &format!(" {pkg} = {pkg},\n");
|
||||
}
|
||||
|
||||
ret += "}\n";
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/lua-build.bin"));
|
||||
let file_descriptor_set =
|
||||
prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap();
|
||||
|
||||
let mut enums = EnumMap::new();
|
||||
let mut msgs = MessageMap::new();
|
||||
|
||||
let mut services = String::new();
|
||||
|
||||
for proto in file_descriptor_set.file.iter() {
|
||||
let package = proto.package().to_string();
|
||||
for r#enum in proto.enum_type.iter() {
|
||||
parse_enum(&mut enums, &package, r#enum);
|
||||
}
|
||||
|
||||
for msg in proto.message_type.iter() {
|
||||
parse_message_enums(&mut enums, &package, msg);
|
||||
parse_message(&mut msgs, &package, msg);
|
||||
}
|
||||
|
||||
for service in proto.service.iter() {
|
||||
services += &populate_service_defs(&package, service);
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
generate_enum_definitions(&enums)
|
||||
+ "\n"
|
||||
+ &generate_message_classes(&msgs)
|
||||
+ "\n"
|
||||
+ &generate_message_tables(&msgs)
|
||||
+ "\n"
|
||||
+ &populate_table_enums(&enums)
|
||||
+ "\n"
|
||||
+ &services
|
||||
+ "\n"
|
||||
+ &generate_returned_table(&msgs)
|
||||
);
|
||||
}
|
File diff suppressed because it is too large
Load diff
15
justfile
15
justfile
|
@ -35,7 +35,7 @@ install-protos:
|
|||
cp -r "{{rootdir}}/api/protocol" "${proto_dir}"
|
||||
|
||||
# Install the Lua library (requires Luarocks)
|
||||
install-lua-lib:
|
||||
install-lua-lib: gen-lua-pb-defs
|
||||
#!/usr/bin/env bash
|
||||
cd "{{rootdir}}/api/lua"
|
||||
luarocks make --local --lua-version "{{lua_version}}"
|
||||
|
@ -83,15 +83,22 @@ install-lua-lib-root:
|
|||
luarocks make --lua-version "{{lua_version}}"
|
||||
|
||||
# Run `cargo build`
|
||||
build *args:
|
||||
build *args: gen-lua-pb-defs
|
||||
cargo build {{args}}
|
||||
|
||||
# Generate the protobuf definitions Lua file
|
||||
gen-lua-pb-defs:
|
||||
#!/usr/bin/env bash
|
||||
set -euxo pipefail
|
||||
cargo build --package lua-build
|
||||
./target/debug/lua-build > "./api/lua/pinnacle/grpc/defs.lua"
|
||||
|
||||
# Run `cargo run`
|
||||
run *args:
|
||||
run *args: gen-lua-pb-defs
|
||||
cargo run {{args}}
|
||||
|
||||
# Run `cargo test`
|
||||
test *args:
|
||||
test *args: gen-lua-pb-defs
|
||||
cargo test {{args}}
|
||||
|
||||
compile-wlcs:
|
||||
|
|
2
snowcap
2
snowcap
|
@ -1 +1 @@
|
|||
Subproject commit 8dc3b93c42e603c5f893b65581f78c805c16daf3
|
||||
Subproject commit b217d37fbf88b07c03632a1a86ac568f84b3040c
|
Loading…
Reference in a new issue