Skip to content

Commit 5681d08

Browse files
committed
Phase 3
1 parent 7808051 commit 5681d08

5 files changed

Lines changed: 471 additions & 23 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ These features are essential for broader ecosystem compatibility:
206206
### Medium Priority
207207
Important for feature completeness and broader compatibility:
208208

209-
- [ ] **Services/RPC** - Support for service definitions
210-
- [ ] Service definition parsing
211-
- [ ] Method definitions
212-
- [ ] Code generation for service stubs
213-
- [ ] Streaming support (client/server/bidirectional)
209+
- **Services/RPC** - Support for service definitions
210+
- Service definition parsing
211+
- Method definitions
212+
- Code generation for service stubs
213+
- Streaming support detection (client/server/bidirectional)
214214
- [ ] **Proto2 support** - Full proto2 compatibility
215215
- [ ] Required/optional field semantics
216216
- [ ] Default values

src/protozoa/codegen.gleam

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ fn generate_file_with_imports(
8080
proto_file.messages,
8181
)
8282

83+
let service_stubs = generate_service_stubs(proto_file.services)
84+
8385
// Combine all sections with proper spacing
8486
let sections =
8587
[
@@ -91,6 +93,7 @@ fn generate_file_with_imports(
9193
message_encoders,
9294
message_decoders,
9395
enum_helpers,
96+
service_stubs,
9497
]
9598
|> list.filter(fn(section) { !string.is_empty(section) })
9699

@@ -481,3 +484,136 @@ fn generate_any_definition() -> String {
481484
]
482485
string.join(lines, "\n")
483486
}
487+
488+
/// Generate service stub definitions for gRPC/HTTP services
489+
fn generate_service_stubs(services: List(parser.Service)) -> String {
490+
case services {
491+
[] -> ""
492+
_ -> {
493+
services
494+
|> list.map(generate_single_service_stub)
495+
|> string.join("\n\n")
496+
}
497+
}
498+
}
499+
500+
/// Generate stub for a single service
501+
fn generate_single_service_stub(service: parser.Service) -> String {
502+
let client_interface = generate_client_interface(service)
503+
let server_interface = generate_server_interface(service)
504+
505+
string.join(
506+
[
507+
"// Service: " <> service.name,
508+
"",
509+
client_interface,
510+
"",
511+
server_interface,
512+
],
513+
"\n",
514+
)
515+
}
516+
517+
/// Generate client interface for service
518+
fn generate_client_interface(service: parser.Service) -> String {
519+
let type_name = service.name <> "Client"
520+
let method_comments = generate_method_comments(service.methods, "client")
521+
522+
string.join(
523+
[
524+
"/// Client interface for " <> service.name <> " service",
525+
"/// This trait defines the client-side methods for calling the service",
526+
"pub type " <> type_name <> " {",
527+
" " <> type_name <> "(",
528+
" // TODO: Add client implementation fields (e.g., HTTP client, endpoint URL)",
529+
" endpoint: String,",
530+
" )",
531+
"}",
532+
"",
533+
method_comments,
534+
"// TODO: Implement actual client method calls",
535+
],
536+
"\n",
537+
)
538+
}
539+
540+
/// Generate server interface for service
541+
fn generate_server_interface(service: parser.Service) -> String {
542+
let type_name = service.name <> "Server"
543+
let method_comments = generate_method_comments(service.methods, "server")
544+
545+
string.join(
546+
[
547+
"/// Server interface for " <> service.name <> " service",
548+
"/// Implement this trait to handle incoming service requests",
549+
"pub type " <> type_name <> " {",
550+
" " <> type_name <> "(",
551+
" // TODO: Add server implementation fields",
552+
" )",
553+
"}",
554+
"",
555+
method_comments,
556+
"// TODO: Implement server method handlers and request routing",
557+
],
558+
"\n",
559+
)
560+
}
561+
562+
/// Generate method signature comments
563+
fn generate_method_comments(
564+
methods: List(parser.Method),
565+
interface_type: String,
566+
) -> String {
567+
let comment_prefix = case interface_type {
568+
"client" -> "// Method signatures for " <> interface_type <> ":"
569+
"server" -> "// Method signatures for " <> interface_type <> ":"
570+
_ -> "// Method signatures:"
571+
}
572+
573+
let method_lines =
574+
list.map(methods, fn(method) {
575+
let streaming_info =
576+
get_streaming_comment(
577+
method.client_streaming,
578+
method.server_streaming,
579+
interface_type,
580+
)
581+
" // "
582+
<> method.name
583+
<> "("
584+
<> method.input_type
585+
<> ") -> "
586+
<> method.output_type
587+
<> " "
588+
<> streaming_info
589+
})
590+
591+
string.join([comment_prefix, ..method_lines], "\n")
592+
}
593+
594+
/// Get streaming type comment for method
595+
fn get_streaming_comment(
596+
client_streaming: Bool,
597+
server_streaming: Bool,
598+
interface_type: String,
599+
) -> String {
600+
let base_type = case interface_type {
601+
"server" ->
602+
"// "
603+
<> case client_streaming, server_streaming {
604+
False, False -> "Unary handler"
605+
False, True -> "Server streaming handler"
606+
True, False -> "Client streaming handler"
607+
True, True -> "Bidirectional streaming handler"
608+
}
609+
_ ->
610+
"// "
611+
<> case client_streaming, server_streaming {
612+
False, False -> "Unary call"
613+
False, True -> "Server streaming"
614+
True, False -> "Client streaming"
615+
True, True -> "Bidirectional streaming"
616+
}
617+
}
618+
base_type
619+
}

src/protozoa/internal/well_known_types.gleam

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ fn timestamp_proto() -> parser.ProtoFile {
4545
),
4646
],
4747
enums: [],
48+
services: [],
4849
)
4950
}
5051

@@ -78,6 +79,7 @@ fn duration_proto() -> parser.ProtoFile {
7879
),
7980
],
8081
enums: [],
82+
services: [],
8183
)
8284
}
8385

@@ -111,6 +113,7 @@ fn any_proto() -> parser.ProtoFile {
111113
),
112114
],
113115
enums: [],
116+
services: [],
114117
)
115118
}
116119

@@ -129,6 +132,7 @@ fn empty_proto() -> parser.ProtoFile {
129132
),
130133
],
131134
enums: [],
135+
services: [],
132136
)
133137
}
134138

@@ -275,6 +279,7 @@ fn wrappers_proto() -> parser.ProtoFile {
275279
),
276280
],
277281
enums: [],
282+
services: [],
278283
)
279284
}
280285

@@ -372,6 +377,7 @@ fn struct_proto() -> parser.ProtoFile {
372377
parser.EnumValue(name: "NULL_VALUE", number: 0),
373378
]),
374379
],
380+
services: [],
375381
)
376382
}
377383

@@ -398,6 +404,7 @@ fn field_mask_proto() -> parser.ProtoFile {
398404
),
399405
],
400406
enums: [],
407+
services: [],
401408
)
402409
}
403410

0 commit comments

Comments
 (0)