ts-proto
Version:
> `ts-proto` transforms your `.proto` files into strongly-typed, idiomatic TypeScript files!
135 lines (120 loc) • 3.91 kB
text/typescript
import { google } from '../build/pbjs';
import FileDescriptorProto = google.protobuf.FileDescriptorProto;
/** This type is expecting a value from the Fields constant. */
export type FieldID = number;
/**
* The field values here represent the proto field IDs associated with the types
* (file,message,enum,service).
*
* For more information read the comments for SourceCodeInfo declared in
* google's 'descriptor.proto' file, see:
* https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L730
*/
export const Fields = {
file: {
syntax: 12,
message_type: 4,
enum_type: 5,
service: 6,
extension: 7,
},
message: {
field: 2,
nested_type: 3,
enum_type: 4,
oneof_decl: 8,
},
enum: {
value: 2,
},
service: {
method: 2,
},
};
/**
* This type is simply an interface on the SourceCodeInfo.Location message.
*/
export interface SourceDescription {
readonly span: number[];
readonly leadingComments: string;
readonly trailingComments: string;
readonly leadingDetachedComments: string[];
}
/** An empty SourceDescription for when one is not available. */
class EmptyDescription implements SourceDescription {
span = [];
leadingComments = '';
trailingComments = '';
leadingDetachedComments = [];
}
/**
* Mapping from a string of dotted notation `path` parts to efficiently
* lookup the related source information.
*/
export type SourceInfoMap = {
[key: string]: SourceDescription;
};
/**
* This class provides direct lookup and navigation through the type
* system by the use of lookup/open to access the source info for types
* defined in a protocol buffer.
*/
export default class SourceInfo implements SourceDescription {
/** Returns an empty SourceInfo */
static empty() {
return new SourceInfo({}, new EmptyDescription());
}
/**
* Creates the SourceInfo from the FileDescriptorProto given to you
* by the protoc compiler. It indexes file.sourceCodeInfo by dotted
* path notation and returns the root SourceInfo.
*/
static fromDescriptor(file: FileDescriptorProto) {
let map: SourceInfoMap = {};
if (file.sourceCodeInfo && file.sourceCodeInfo.location) {
file.sourceCodeInfo.location.forEach((loc) => {
map[loc.path.join('.')] = loc;
});
}
return new SourceInfo(map, new EmptyDescription());
}
// Private
private constructor(
private readonly sourceCode: SourceInfoMap,
private readonly selfDescription: SourceDescription
) {}
/** Returns the code span [start line, start column, end line] */
get span() {
return this.selfDescription.span;
}
/** Leading consecutive comment lines prior to the current element */
get leadingComments() {
return this.selfDescription.leadingComments;
}
/** Documentation is unclear about what exactly this is */
get trailingComments() {
return this.selfDescription.trailingComments;
}
/** Detached comments are those preceeding but separated by a blank non-comment line */
get leadingDetachedComments() {
return this.selfDescription.leadingDetachedComments;
}
/** Return the source info for the field id and index specficied */
lookup(type: FieldID, index?: number): SourceDescription {
if (index === undefined) {
return this.sourceCode[`${type}`] || new EmptyDescription();
}
return this.sourceCode[`${type}.${index}`] || new EmptyDescription();
}
/** Returns a new SourceInfo class representing the field id and index specficied */
open(type: FieldID, index: number): SourceInfo {
const prefix = `${type}.${index}.`;
const map: SourceInfoMap = {};
Object.keys(this.sourceCode)
.filter((key) => key.startsWith(prefix))
.forEach((key) => {
map[key.substr(prefix.length)] = this.sourceCode[key];
});
return new SourceInfo(map, this.lookup(type, index));
}
}