Skip to content

Commit

Permalink
feat: implement library and binary/json serialization
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Stewart <christian@aperture.us>
  • Loading branch information
paralin committed Apr 25, 2024
1 parent 1d58e1f commit ec8fcc3
Show file tree
Hide file tree
Showing 19 changed files with 3,089 additions and 6 deletions.
2 changes: 1 addition & 1 deletion bin/protoc-gen-es-lite
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

const {runNodeJs} = require("@bufbuild/protoplugin");
const {protocGenEsLite} = require("../dist/cjs/src/protoc-gen-es-lite-plugin.js");
const {protocGenEsLite} = require("../dist/src/protoc-gen-es-lite-plugin.js");

runNodeJs(protocGenEsLite);
88 changes: 88 additions & 0 deletions example/example-v2_pb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @generated by protoc-gen-es-lite v0.0.0 with parameter "target=ts,ts_nocheck=false"
// @generated from file example/example.proto (package example, syntax proto3)
/* eslint-disable */

import { newFieldList, PartialMessage, MessageType } from "../src"; // "@aptre/protobuf-es-lite";

export const protobufPackage = 'echo'

/**
* ExampleEnum is an example enumeration.
*
* @generated from enum example.ExampleEnum
*/
export enum ExampleEnum {
/**
* @generated from enum value: UNKNOWN = 0;
*/
UNKNOWN = 0,

/**
* @generated from enum value: FIRST = 1;
*/
FIRST = 1,

/**
* @generated from enum value: SECOND = 2;
*/
SECOND = 2,
}

// ExampleEnum_Name maps the enum names to the values.
export const ExampleEnum_Name = {
UNKNOWN: ExampleEnum.UNKNOWN,
FIRST: ExampleEnum.FIRST,
SECOND: ExampleEnum.SECOND,
}

/**
* EchoMsg is the message body for Echo.
*
* @generated from message example.EchoMsg
*/
export interface EchoMsg implements Message<EchoMsg> {
/**
* @generated from field: string body = 1;
*/
body: string
}

export const EchoMsg: MessageType<EchoMsg> = {
typeName = "example.EchoMsg",

fields: newFieldList(() => [
{ no: 1, name: "body", kind: "scalar", T: 9 /* ScalarType.STRING */ },
], true),

create(partial?: PartialMessage<EchoMsg>): EchoMsg {
const out: PartialMessage<EchoMsg> = {}
proto3.util.initPartial(partial, out);
return out
},

fromBinary(data: Uint8Array, options?: Partial<BinaryReadOptions>): EchoMsg {
const type = EchoMsg,
format = type.runtime.bin,
opt = format.makeReadOptions(options);
format.readMessage(this, opt.readerFactory(data), data.byteLength, opt);
return this;
},

fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EchoMsg {
const type = EchoMsg,
format = type.runtime.json,
opt = format.makeReadOptions(options);
format.readMessage(type, jsonValue, opt, this as unknown as EchoMsg);
return this;
}

fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EchoMsg;

equals(other: EchoMsg | PlainMessage<T> | undefined | null): boolean {
return this.getType().runtime.util.equals(
this.getType(),
this as unknown as T,
other,
);
}
} as const
11 changes: 11 additions & 0 deletions example/example.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
syntax = "proto3";
package example;

import "google/protobuf/timestamp.proto";

// Echoer service returns the given message.
service Echoer {
// Echo returns the given message.
Expand All @@ -13,7 +15,16 @@ service Echoer {
rpc EchoBidiStream(stream EchoMsg) returns (stream EchoMsg);
}

// ExampleEnum is an example enumeration.
enum ExampleEnum {
UNKNOWN = 0;
FIRST = 1;
SECOND = 2;
}

// EchoMsg is the message body for Echo.
message EchoMsg {
string body = 1;
google.protobuf.Timestamp ts = 2;
ExampleEnum example_enum = 3;
}
86 changes: 86 additions & 0 deletions example/example_pb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// @generated by protoc-gen-es-lite v0.0.0 with parameter "target=ts,ts_nocheck=false"
// @generated from file example/example.proto (package example, syntax proto3)
/* eslint-disable */

import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3, Timestamp } from "@bufbuild/protobuf";

/**
* ExampleEnum is an example enumeration.
*
* @generated from enum example.ExampleEnum
*/
export enum ExampleEnum {
/**
* @generated from enum value: UNKNOWN = 0;
*/
UNKNOWN = 0,

/**
* @generated from enum value: FIRST = 1;
*/
FIRST = 1,

/**
* @generated from enum value: SECOND = 2;
*/
SECOND = 2,
}
// Retrieve enum metadata with: proto3.getEnumType(ExampleEnum)
proto3.util.setEnumType(ExampleEnum, "example.ExampleEnum", [
{ no: 0, name: "UNKNOWN" },
{ no: 1, name: "FIRST" },
{ no: 2, name: "SECOND" },
]);

/**
* EchoMsg is the message body for Echo.
*
* @generated from message example.EchoMsg
*/
export class EchoMsg extends Message<EchoMsg> {
/**
* @generated from field: string body = 1;
*/
body = "";

/**
* @generated from field: google.protobuf.Timestamp ts = 2;
*/
ts?: Timestamp;

/**
* @generated from field: example.ExampleEnum example_enum = 3;
*/
exampleEnum = ExampleEnum.UNKNOWN;

constructor(data?: PartialMessage<EchoMsg>) {
super();
proto3.util.initPartial(data, this);
}

static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "example.EchoMsg";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "body", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "ts", kind: "message", T: Timestamp },
{ no: 3, name: "example_enum", kind: "enum", T: proto3.getEnumType(ExampleEnum) },
]);

static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EchoMsg {
return new EchoMsg().fromBinary(bytes, options);
}

static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EchoMsg {
return new EchoMsg().fromJson(jsonValue, options);
}

static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EchoMsg {
return new EchoMsg().fromJsonString(jsonString, options);
}

static equals(a: EchoMsg | PlainMessage<EchoMsg> | undefined, b: EchoMsg | PlainMessage<EchoMsg> | undefined): boolean {
return proto3.util.equals(EchoMsg, a, b);
}
}

19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,29 @@
"repository": {
"url": "git+ssh://git@github.com/aperturerobotics/protobuf-es-lite.git"
},
"files": [
".ignore",
"LICENSE",
"README.md",
"bin/protoc-gen-es-lite",
"example/example.proto",
"example/example.pb.ts",
"tsconfig.base.json",
"tsconfig.json",
"dist"
],
"bin": {
"protoc-gen-es-lite": "bin/protoc-gen-es-lite"
},
"engines": {
"node": ">=14"
},
"scripts": {
"clean": "rimraf ./dist/cjs/*",
"build": "tsc --project tsconfig.json --outDir ./dist/cjs"
"clean": "rimraf ./dist",
"build": "npm run clean && tsc --project tsconfig.json --outDir ./dist",
"typecheck": "tsc --noEmit --project tsconfig.json --outDir ./dist",
"example": "npm run build && protoc --plugin=./bin/protoc-gen-es-lite --es-lite_out=. --es-lite_opt target=ts --es-lite_opt ts_nocheck=false ./example/example.proto",
"format": "prettier --write './src/**/(*.ts|*.tsx|*.html|*.css|*.scss)'"
},
"preferUnplugged": true,
"dependencies": {
Expand All @@ -30,6 +44,7 @@
}
},
"devDependencies": {
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"typescript": "^5.4.5"
}
Expand Down
59 changes: 59 additions & 0 deletions src/assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2021-2024 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* Assert that condition is truthy or throw error (with message)
*/
export function assert(condition: unknown, msg?: string): asserts condition {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions -- we want the implicit conversion to boolean
if (!condition) {
throw new Error(msg);
}
}

const FLOAT32_MAX = 3.4028234663852886e38,
FLOAT32_MIN = -3.4028234663852886e38,
UINT32_MAX = 0xffffffff,
INT32_MAX = 0x7fffffff,
INT32_MIN = -0x80000000;

/**
* Assert a valid signed protobuf 32-bit integer.
*/
export function assertInt32(arg: unknown): asserts arg is number {
if (typeof arg !== "number") throw new Error("invalid int 32: " + typeof arg);
if (!Number.isInteger(arg) || arg > INT32_MAX || arg < INT32_MIN)
throw new Error("invalid int 32: " + arg); // eslint-disable-line @typescript-eslint/restrict-plus-operands -- we want the implicit conversion to string
}

/**
* Assert a valid unsigned protobuf 32-bit integer.
*/
export function assertUInt32(arg: unknown): asserts arg is number {
if (typeof arg !== "number")
throw new Error("invalid uint 32: " + typeof arg);
if (!Number.isInteger(arg) || arg > UINT32_MAX || arg < 0)
throw new Error("invalid uint 32: " + arg); // eslint-disable-line @typescript-eslint/restrict-plus-operands -- we want the implicit conversion to string
}

/**
* Assert a valid protobuf float value.
*/
export function assertFloat32(arg: unknown): asserts arg is number {
if (typeof arg !== "number")
throw new Error("invalid float 32: " + typeof arg);
if (!Number.isFinite(arg)) return;
if (arg > FLOAT32_MAX || arg < FLOAT32_MIN)
throw new Error("invalid float 32: " + arg); // eslint-disable-line @typescript-eslint/restrict-plus-operands -- we want the implicit conversion to string
}
Loading

0 comments on commit ec8fcc3

Please sign in to comment.