Source: client/any-packer.js

  1. /*
  2. * Copyright 2023, TeamDev. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Redistribution and use in source and/or binary forms, with or without
  11. * modification, must retain the above copyright notice and the following
  12. * disclaimer.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. "use strict";
  27. import {Message} from 'google-protobuf';
  28. import {isProtobufMessage, Type, TypedMessage} from './typed-message';
  29. import {Any} from '../proto/google/protobuf/any_pb';
  30. /**
  31. * An packer of string, number, boolean, and message values from Protobuf `Any`.
  32. */
  33. class Unpack {
  34. /**
  35. * @param {Any} any a Protobuf `Any` value to be unpacked to message or primitive
  36. */
  37. constructor(any) {
  38. /**
  39. * @type {Any}
  40. * @private
  41. */
  42. this._any = any;
  43. }
  44. /**
  45. * Unpacks a Protobuf `Any` assuming it contains a string value.
  46. *
  47. * @return {String}
  48. */
  49. asString() {
  50. return this.as(Type.STRING).getValue();
  51. }
  52. /**
  53. * Unpacks a Protobuf `Any` assuming it contains an int32 value.
  54. *
  55. * @return {Number}
  56. */
  57. asInt32() {
  58. return this.as(Type.INT32).getValue();
  59. }
  60. /**
  61. * Unpacks a Protobuf `Any` assuming it contains an unsigned int32 value.
  62. *
  63. * @return {Number}
  64. */
  65. asUInt32() {
  66. return this.as(Type.UINT32).getValue();
  67. }
  68. /**
  69. * Unpacks a Protobuf `Any` assuming it contains an int64 value.
  70. *
  71. * @return {Number}
  72. */
  73. asInt64() {
  74. return this.as(Type.INT64).getValue();
  75. }
  76. /**
  77. * Unpacks a Protobuf `Any` assuming it contains an unsigned int64 value.
  78. *
  79. * @return {Number}
  80. */
  81. asUInt64() {
  82. return this.as(Type.UINT64).getValue();
  83. }
  84. /**
  85. * Unpacks a Protobuf `Any` assuming it contains a boolean value.
  86. *
  87. * @return {boolean}
  88. */
  89. asBool() {
  90. return this.as(Type.BOOL).getValue();
  91. }
  92. /**
  93. * Unpacks a Protobuf `Any` assuming it contains a double value.
  94. *
  95. * @return {number}
  96. */
  97. asDouble() {
  98. return this.as(Type.DOUBLE).getValue();
  99. }
  100. /**
  101. * Unpacks a Protobuf `Any` assuming it contains a float value.
  102. *
  103. * @return {number}
  104. */
  105. asFloat() {
  106. return this.as(Type.FLOAT).getValue();
  107. }
  108. /**
  109. * Unpacks a Protobuf `Any` assuming it a message of the provided type.
  110. *
  111. * @param {!Type} type a type used to deserialize `Any` encompassed in this unpacker
  112. *
  113. * @return {Message} a protobuf message wrapped in `Any` deserialized using the provided type
  114. */
  115. as(type) {
  116. return this._any.unpack(type.class().deserializeBinary, type.url().name());
  117. }
  118. }
  119. /**
  120. * A packer of string, number, boolean, and message values to Protobuf `Any`.
  121. */
  122. class Pack {
  123. /**
  124. * @param {*} value
  125. */
  126. constructor(value) {
  127. this._value = value;
  128. }
  129. /**
  130. * Packs the encompassed value assuming its of a string type.
  131. *
  132. * @return {Any} a new any instance with this packers value
  133. */
  134. asString() {
  135. return Pack._primitive(this._value.toString(), Type.STRING);
  136. }
  137. /**
  138. * Packs the encompassed value assuming its of an int32 type.
  139. *
  140. * @return {Any} a new any instance with this packers value
  141. */
  142. asInt32() {
  143. const value = Number(this._value);
  144. if (isNaN(value)) {
  145. throw new Error('The value could not be coerced to a number');
  146. }
  147. return Pack._primitive(Math.floor(value.valueOf()), Type.INT32);
  148. }
  149. /**
  150. * Packs the encompassed value assuming its of an unsigned int32 type.
  151. *
  152. * @return {Any} a new any instance with this packers value
  153. */
  154. asUInt32() {
  155. const value = Number(this._value);
  156. if (isNaN(value)) {
  157. throw new Error('The value could not be coerced to a number');
  158. }
  159. return Pack._primitive(Math.floor(value.valueOf()), Type.UINT32);
  160. }
  161. /**
  162. * Packs the encompassed value assuming its of an int64 type.
  163. *
  164. * @return {Any} a new any instance with this packers value
  165. */
  166. asInt64() {
  167. const value = Number(this._value);
  168. if (isNaN(value)) {
  169. throw new Error('The value could not be coerced to a number');
  170. }
  171. return Pack._primitive(Math.floor(value.valueOf()), Type.INT64);
  172. }
  173. /**
  174. * Packs the encompassed value assuming its of an unsigned int64 type.
  175. *
  176. * @return {Any} a new any instance with this packers value
  177. */
  178. asUInt64() {
  179. const value = Number(this._value);
  180. if (isNaN(value)) {
  181. throw new Error('The value could not be coerced to a number');
  182. }
  183. return Pack._primitive(Math.floor(value.valueOf()), Type.UINT64);
  184. }
  185. /**
  186. * Packs the encompassed value assuming its of a float type.
  187. *
  188. * @return {Any} a new any instance with this packers value
  189. */
  190. asFloat() {
  191. const value = Number(this._value);
  192. if (isNaN(value)) {
  193. throw new Error('The value could not be coerced to a number');
  194. }
  195. return Pack._primitive(value.valueOf(), Type.FLOAT);
  196. }
  197. /**
  198. * Packs the encompassed value assuming its of a double type.
  199. *
  200. * @return {Any} a new any instance with this packers value
  201. */
  202. asDouble() {
  203. const value = Number(this._value);
  204. if (isNaN(value)) {
  205. throw new Error('The value could not be coerced to a number');
  206. }
  207. return Pack._primitive(value.valueOf(), Type.DOUBLE);
  208. }
  209. /**
  210. * Packs the encompassed value assuming its of a boolean type.
  211. *
  212. * @return {Any} a new any instance with this packers value
  213. */
  214. asBool() {
  215. return Pack._primitive(!!this._value, Type.BOOL);
  216. }
  217. /**
  218. * @param {!Type} type a type to pack the value encompassed in this packer to
  219. *
  220. * @return {Any} a new any instance with this packers value
  221. */
  222. as(type) {
  223. return Pack._message(this._value, type);
  224. }
  225. /**
  226. * Packs a primitive (number, string, boolean) value to Protobuf `Any`.
  227. *
  228. * @param {!number|string|boolean} value a primitive value to pack
  229. * @param {!Type} type definition of the type to wrap the value
  230. *
  231. * @return {Any} a new `Any`
  232. *
  233. * @private
  234. */
  235. static _primitive(value, type) {
  236. const wrapper = type.class();
  237. const message = new wrapper([value]);
  238. return Pack._message(message, type);
  239. }
  240. /**
  241. * Packs a Protobuf message to Protobuf `Any`.
  242. *
  243. * @param {!Message} message a message to be packed into `Any`
  244. * @param {!Type} type definition of the Message type
  245. *
  246. * @return {Any} a new `Any`
  247. *
  248. * @private
  249. */
  250. static _message(message, type) {
  251. const typeUrl = type.url();
  252. const result = new Any();
  253. const bytes = message.serializeBinary();
  254. result.pack(bytes, typeUrl.name(), typeUrl.prefix());
  255. return result;
  256. }
  257. }
  258. /**
  259. * Utilities for packing messages into {@link Any} and unpacking them.
  260. *
  261. * @example
  262. * // Packing `TypedMessage` instance:
  263. * const task = new Task(message, TASK_TYPE);
  264. * const anyWithTask = AnyPacker.packTyped(task);
  265. *
  266. * @example
  267. * // Packing Protobuf messages:
  268. * const anyWithTask = AnyPacker.pack(message).as(TASK_TYPE);
  269. * const task = AnyPacker.unpack(anyWithTask).as(TASK_TYPE);
  270. *
  271. * @example
  272. * // Packing strings:
  273. * const anyWithString = AnyPacker.pack('Presents get packed too!').asString();
  274. * console.log(AnyPacker.unpack(anyWithString).asString());
  275. * // Out: Presents get packed too!
  276. */
  277. export class AnyPacker {
  278. /**
  279. * Instantiation not allowed and will throw an error.
  280. */
  281. constructor() {
  282. throw new Error('Tried instantiating a utility class.');
  283. }
  284. /**
  285. * Creates a new packer for the provided `Any` instance.
  286. *
  287. * @example
  288. * // Unpacking messages:
  289. * AnyPacker.unpack(anyWithTask).as(TASK_TYPE);
  290. *
  291. * @example
  292. * // Unpacking primitive values:
  293. * AnyPacker.pack('Presents get packed too!').asString();
  294. *
  295. * @param {!Any} any a Protobuf `Any` message to unpack
  296. *
  297. * @return {Unpack} an unpacker for the provided `Any` instance
  298. */
  299. static unpack(any) {
  300. return new Unpack(any);
  301. }
  302. /**
  303. * Creates a new packer for the provided value.
  304. *
  305. * @example
  306. * // Packing primitive values:
  307. * AnyPacker.pack('Presents get packed too!').asString();
  308. *
  309. * @example
  310. * // Packing Protobuf messages:
  311. * const anyWithTask = AnyPacker.pack(message).as(TASK_TYPE);
  312. *
  313. * @param {!*} value a value of
  314. *
  315. * @return {Pack}
  316. */
  317. static pack(value) {
  318. return new Pack(value);
  319. }
  320. /**
  321. * Packs a `Message` into a Protobuf `Any`.
  322. *
  323. * @param {!Message} message a message to be packed
  324. *
  325. * @return {Any} a new Any with the provided message inside
  326. */
  327. static packMessage(message) {
  328. if (!isProtobufMessage(message)) {
  329. throw new Error('The `Message` type was expected by AnyPacker#packMessage().');
  330. }
  331. const typedMessage = TypedMessage.of(message);
  332. return this.packTyped(typedMessage);
  333. }
  334. /**
  335. * Packs a `TypedMessage` into a Protobuf `Any`.
  336. *
  337. * @param {!TypedMessage} message a message to be packed
  338. *
  339. * @return {Any} a new Any with the provided typed message inside
  340. */
  341. static packTyped(message) {
  342. if (!(message instanceof TypedMessage)) {
  343. throw new Error('Only TypedMessage instance can be packed using AnyPacker#packTyped().');
  344. }
  345. return new Pack(message.message).as(message.type);
  346. }
  347. }