// The two registries: files by path and declaration, and types by name. The
// global pair is what a generated file registers into at module load.
///|
/// A registry refusal: a name already taken.
pub(all) suberror Refused {
Conflict(String)
}
///|
pub impl Show for Refused with fn output(self, logger) {
match self {
Conflict(name) => logger.write_string("already registered: " + name)
}
}
///|
pub extend Refused with Show::{to_string, output}
///|
/// File descriptors, looked up by path or by a declaration's qualified name.
pub struct Files {
files : Array[FileDescriptor]
}
///|
pub fn Files::new() -> Files {
{ files: [], }
}
///|
pub fn Files::register(
self : Files,
file : FileDescriptor,
) -> Unit raise Refused {
for existing in self.files {
if existing.path == file.path {
raise Conflict(file.path)
}
}
self.files.push(file)
}
///|
pub fn Files::length(self : Files) -> Int32 {
self.files.length()
}
///|
pub fn Files::all(self : Files) -> Array[FileDescriptor] {
self.files
}
///|
pub fn Files::find_file_by_path(self : Files, path : String) -> FileDescriptor? {
for file in self.files {
if file.path == path {
return Some(file)
}
}
None
}
///|
pub fn Files::find_message_by_name(
self : Files,
name : String,
) -> MessageDescriptor? {
for file in self.files {
match find_message_in(file.messages, name) {
Some(message) => return Some(message)
None => ()
}
}
None
}
///|
fn find_message_in(
messages : Array[MessageDescriptor],
name : String,
) -> MessageDescriptor? {
for message in messages {
if message.full_name == name {
return Some(message)
}
match find_message_in(message.nested_messages, name) {
Some(found) => return Some(found)
None => ()
}
}
None
}
///|
pub fn Files::find_enum_by_name(self : Files, name : String) -> EnumDescriptor? {
for file in self.files {
match find_enum_in(file.enums, name) {
Some(found) => return Some(found)
None => ()
}
for message in file.messages {
match find_enum_in(message.nested_enums, name) {
Some(found) => return Some(found)
None => ()
}
}
}
None
}
///|
fn find_enum_in(
enums : Array[EnumDescriptor],
name : String,
) -> EnumDescriptor? {
for item in enums {
if item.full_name == name {
return Some(item)
}
}
None
}
///|
/// Message, enum and extension types, looked up by qualified name.
pub struct Types {
messages : Array[(String, &MessageType)]
enums : Array[(String, &EnumType)]
extensions : Array[(String, &ExtensionType)]
extensions_by_number : Array[(String, &ExtensionType)]
}
///|
pub fn Types::new() -> Types {
{ messages: [], enums: [], extensions: [], extensions_by_number: [], }
}
///|
fn lookup_message(
entries : Array[(String, &MessageType)],
name : String,
) -> &MessageType? {
for entry in entries {
if entry.0 == name {
return Some(entry.1)
}
}
None
}
///|
fn lookup_enum(
entries : Array[(String, &EnumType)],
name : String,
) -> &EnumType? {
for entry in entries {
if entry.0 == name {
return Some(entry.1)
}
}
None
}
///|
fn lookup_extension(
entries : Array[(String, &ExtensionType)],
name : String,
) -> &ExtensionType? {
for entry in entries {
if entry.0 == name {
return Some(entry.1)
}
}
None
}
///|
pub fn Types::register_message(
self : Types,
message : &MessageType,
) -> Unit raise Refused {
let name = message.descriptor().full_name
if lookup_message(self.messages, name) is Some(_) {
raise Conflict(name)
}
self.messages.push((name, message))
}
///|
pub fn Types::register_enum(
self : Types,
item : &EnumType,
) -> Unit raise Refused {
let name = item.descriptor().full_name
if lookup_enum(self.enums, name) is Some(_) {
raise Conflict(name)
}
self.enums.push((name, item))
}
///|
pub fn Types::register_extension(
self : Types,
item : &ExtensionType,
) -> Unit raise Refused {
let field = item.descriptor()
if lookup_extension(self.extensions, field.full_name) is Some(_) {
raise Conflict(field.full_name)
}
self.extensions.push((field.full_name, item))
let key = extension_key(field.containing_message, field.number)
self.extensions_by_number.push((key, item))
}
///|
fn extension_key(message : String, number : Int32) -> String {
message + "#" + number.to_string()
}
///|
pub fn Types::find_message_by_name(
self : Types,
name : String,
) -> &MessageType? {
lookup_message(self.messages, name)
}
///|
pub fn Types::find_enum_by_name(self : Types, name : String) -> &EnumType? {
lookup_enum(self.enums, name)
}
///|
pub fn Types::find_extension_by_name(
self : Types,
name : String,
) -> &ExtensionType? {
lookup_extension(self.extensions, name)
}
///|
pub fn Types::find_extension_by_number(
self : Types,
message : String,
number : Int32,
) -> &ExtensionType? {
lookup_extension(self.extensions_by_number, extension_key(message, number))
}
///|
pub fn Types::num_messages(self : Types) -> Int32 {
self.messages.length()
}
///|
pub fn Types::num_enums(self : Types) -> Int32 {
self.enums.length()
}
///|
pub fn Types::num_extensions(self : Types) -> Int32 {
self.extensions.length()
}
///|
pub fn Types::range_messages(self : Types, f : (&MessageType) -> Bool) -> Unit {
for entry in self.messages {
if !f(entry.1) {
break
}
}
}
///|
pub fn Types::range_enums(self : Types, f : (&EnumType) -> Bool) -> Unit {
for entry in self.enums {
if !f(entry.1) {
break
}
}
}
///|
pub fn Types::range_extensions(
self : Types,
f : (&ExtensionType) -> Bool,
) -> Unit {
for entry in self.extensions {
if !f(entry.1) {
break
}
}
}
///|
/// The process-wide registries a generated file registers into.
pub let global_files : Files = Files::new()
///|
pub let global_types : Types = Types::new()