-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
749b961
commit e0bbda4
Showing
4 changed files
with
234 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
(** Module to create mapping between proto names 'X.Y.Z' to ocaml names *) | ||
open !StdLabels | ||
open !MoreLabels | ||
open !Utils | ||
open Spec.Descriptor.Google.Protobuf | ||
|
||
|
||
let sprintf = Printf.sprintf | ||
|
||
type oneof = { name: string; constructor_name: string; type_: string option; } | ||
type field_type = Plain of { type_: string option } | ||
| Oneof of oneof list | ||
|
||
type constructor = { name: string; ocaml_name: string } | ||
type field = { name: string; ocaml_name: string; type_: field_type } | ||
|
||
(* The map has: proto_type -> ocaml_module * ocaml_name * element_type *) | ||
|
||
type element_type = | ||
| Message of field list | ||
| Enum of constructor list | ||
| Service | ||
| Method | ||
|
||
|
||
(* The map has: proto_type -> ocaml_module * ocaml_name * element_type *) | ||
|
||
type element = { ocaml_module: string; (* Module in which the definition resides - derived from the proto name *) | ||
ocaml_name: string; (* Fully qualified ocaml name of the module; e.g. My_module.Sub_module.X *) | ||
element_type: element_type; | ||
} | ||
|
||
type t = element StringMap.t | ||
|
||
|
||
let module_name_of_proto ?package proto_file = | ||
Filename.chop_extension proto_file | ||
|> Filename.basename | ||
|> ( | ||
match package with | ||
| Some package -> Printf.sprintf "%s_%s" package | ||
| None -> fun s -> s | ||
) | ||
|> String.capitalize_ascii | ||
|> String.map ~f:(function '-' | '.' -> '_' | c -> c) | ||
|
||
type scope = { proto_path: string; ocaml_path: string; module_name: string } | ||
|
||
let add_scope ~proto_name ~ocaml_name { proto_path; ocaml_path; module_name } = | ||
{ proto_path = sprintf "%s.%s" proto_path proto_name; | ||
ocaml_path = sprintf "%s.%s" ocaml_path ocaml_name; | ||
module_name } | ||
|
||
let element_of_message ~mangle_f ~scope fields oneof_decls = | ||
(* TODO: Old code also branched on FieldDescriptorProto.{ proto3_optional = Some true; _ } *) | ||
let plain_fields = List.filter ~f:(fun FieldDescriptorProto.{ oneof_index; _ } -> Option.is_none oneof_index) fields in | ||
let field_name_map = | ||
let plain_field_names = List.filter_map ~f:(fun field -> field.FieldDescriptorProto.name) plain_fields in | ||
let oneof_names = List.filter_map ~f:(fun field -> field.OneofDescriptorProto.name) oneof_decls in | ||
Names.create_name_map | ||
~standard_f:(Names.field_name ~mangle_f:(fun x -> x)) | ||
~mangle_f:(Names.field_name ~mangle_f) | ||
(plain_field_names @ oneof_names) | ||
in | ||
let oneofs = | ||
List.mapi ~f:(fun i OneofDescriptorProto.{ name; _ } -> | ||
let name = Option.value_exn ~message:"Oneof field must have a name" name in | ||
(* Get all the fields *) | ||
let oneof_fields = | ||
List.filter ~f:(function | ||
| FieldDescriptorProto.{ oneof_index = Some i'; _ } -> i = i' | ||
| FieldDescriptorProto.{ oneof_index = None; _ } -> false | ||
) fields | ||
in | ||
let oneof_name_map = | ||
List.filter_map ~f:(fun field -> field.FieldDescriptorProto.name) oneof_fields | ||
|> Names.create_name_map | ||
~standard_f:(Names.poly_constructor_name ~mangle_f:(fun x -> x)) | ||
~mangle_f:(Names.poly_constructor_name ~mangle_f) | ||
in | ||
let oneofs = | ||
List.map ~f:(fun FieldDescriptorProto.{ name; type_name; type'; _ } -> | ||
let name = Option.value_exn ~message:"All fields should have a name" name in | ||
let type_ = match type' with | ||
| Some FieldDescriptorProto.Type.TYPE_MESSAGE -> type_name | ||
| _ -> None | ||
in | ||
let constructor_name = StringMap.find name oneof_name_map in | ||
{ name; constructor_name; type_; } | ||
) oneof_fields | ||
in | ||
let ocaml_name = StringMap.find name field_name_map in | ||
{ name; ocaml_name; type_ = Oneof oneofs } | ||
) oneof_decls | ||
in | ||
let plain_fields = | ||
List.map ~f:(fun FieldDescriptorProto.{ name; type_name; type'; _ } -> | ||
let name = Option.value_exn ~message:"All fields should have a name" name in | ||
let ocaml_name = StringMap.find name field_name_map in | ||
let type_ = match type' with | ||
| Some FieldDescriptorProto.Type.TYPE_MESSAGE -> type_name | ||
| _ -> None | ||
in | ||
{ name; ocaml_name; type_ = Plain { type_; } } | ||
) plain_fields | ||
in | ||
let fields = plain_fields @ oneofs in | ||
{ ocaml_module = scope.ocaml_module; | ||
ocaml_name = scope.ocaml_name; | ||
element_type = Message fields; | ||
} | ||
|
||
let traverse_message ~mangle_f ~scope map messages = | ||
(* Assign a name for all the messages *) | ||
let names = | ||
let messages = | ||
List.map ~f:(function | ||
| DescriptorProto.{ name = Some name; _ } as desciptor -> name, descriptor | ||
| DescriptorProto.{ name = None; _ } -> failwith "All messages must have a name" | ||
) | ||
in | ||
let names, _ = List.split messages in | ||
let name_map = | ||
Names.create_name_map | ||
~standard_f:(Names.module_name ~mangle_f:(fun x -> x)) | ||
~mangle_f:(Names.module_name ~mangle_f) names | ||
in | ||
List.fold_left ~init:map ~f:(fun (proto_name, desciptor) -> | ||
let ocaml_name = StringMap.find proto_name in | ||
let scope = add_scope ~proto_name ~ocaml_name scope in | ||
|
||
(* Construct the element here *) | ||
(* We need to create a mapping for all fields *) | ||
(* And collect dependencies *) | ||
map | ||
|
||
|
||
scope, desciptor | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
(* When traversing into a scope | ||
let scope' = add_scope name | ||
let message_element = { ocaml_module | ||
let traverse_file ~prefix_module_names ~mangle_f map FileDescriptorProto.{ name; message_type = messages; package; enum_type = enums; service = services; extension = extensions; _ } = | ||
let name = Option.value_exn ~message:"All files must have a name" name in | ||
(* Name is the proto name *) | ||
let module_name = | ||
let package = match prefix_module_names with | ||
| false -> None | ||
| true -> map.package | ||
in | ||
module_name_of_proto ?package map.file_name | ||
in | ||
(* Scope should be the fully qualified ocaml name as well as the mapped ocaml name *) | ||
let scope = | ||
let proto_path = Option.value ~default "" package in | ||
let ocaml_path = | ||
String.split_on_char ~sep:"." proto_path | ||
|> List.map ~f:(Names.module_name ~mangle_f) | ||
|> String.concat ~sep:"." | ||
in | ||
{ proto_path; ocaml_path; module_name } | ||
in | ||
(* Start traversing *) | ||
let traverse_ | ||
DescriptorProto.{ name; field = fields; nested_type = nested_types; enum_type = enums; oneof_decl = oneof_decls; extension = extensions; _} = | ||
let name = Option.value_exn ~message:"All messages must have a name" name in |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters