/**
* MessageUtils.ets
*
* 消息工具函数集合
*
* 提供常用的批量操作、过滤、查找等实用功能
*
* 设计理念:
* - 纯函数设计,无副作用
* - 泛型支持,类型安全
* - 符合 ArkTS 2025 规范
*
* Version: 1.0.0
* ArkTS 2025 兼容
*/
import { Message } from './Message'
/**
* 消息工具函数类
*
* 提供批量操作、过滤、分组等实用功能
*
* 所有方法都是静态方法,无需实例化
*/
export class MessageUtils {
// ========== 批量操作 ==========
/**
* 批量序列化消息为二进制
*
* @param messages 消息数组
* @returns 二进制数据数组
*
* 使用示例:
* ```typescript
* const people = [person1, person2, person3]
* const binaries = MessageUtils.toBinaryBatch(people)
* ```
*/
static toBinaryBatch<T extends Message>(messages: T[]): Uint8Array[] {
return messages.map(m => m.toBinary())
}
/**
* 批量转换消息为 JSON
*
* @param messages 消息数组
* @returns JSON 对象数组
*
* 使用示例:
* ```typescript
* const people = [person1, person2, person3]
* const jsonArray = MessageUtils.toJsonBatch(people)
* ```
*/
static toJsonBatch<T extends Message>(messages: T[]): Record<string, Object>[] {
return messages.map(m => m.toJson())
}
/**
* 批量克隆消息
*
* @param messages 消息数组
* @returns 克隆后的消息数组
*
* 使用示例:
* ```typescript
* const people = [person1, person2, person3]
* const clonedPeople = MessageUtils.cloneBatch(people)
* ```
*/
static cloneBatch<T extends Message>(messages: T[]): T[] {
return messages.map(m => m.clone() as T)
}
// ========== 合并操作 ==========
/**
* 深度合并多个消息(后面的覆盖前面的)
*
* 合并语义(Protobuf 标准):
* - 标量字段:后面的覆盖前面的
* - repeated 字段:追加(不是替换!)
* - 嵌套消息:递归合并
*
* @param messages 消息数组
* @returns 合并后的消息,如果数组为空则返回 null
*
* 使用示例:
* ```typescript
* const base = Person.create({ name: 'Alice', age: 30 })
* const update1 = Person.create({ age: 31 })
* const update2 = Person.create({ emails: ['alice@example.com'] })
* const merged = MessageUtils.mergeAll([base, update1, update2])
* // merged: { name: 'Alice', age: 31, emails: ['alice@example.com'] }
* ```
*/
static mergeAll<T extends Message>(messages: T[]): T | null {
if (messages.length === 0) {
return null
}
const result = messages[0].clone() as T
for (let i = 1; i < messages.length; i++) {
result.mergeFrom(messages[i])
}
return result
}
// ========== 验证和过滤 ==========
/**
* 过滤有效消息
*
* @param messages 消息数组
* @returns 只包含有效消息的数组
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* const validPeople = MessageUtils.filterValid(people)
* // validPeople: [person1, person3]
* ```
*/
static filterValid<T extends Message>(messages: T[]): T[] {
return messages.filter(m => m.isValid())
}
/**
* 过滤无效消息
*
* @param messages 消息数组
* @returns 只包含无效消息的数组
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* const invalidPeople = MessageUtils.filterInvalid(people)
* // invalidPeople: [invalidPerson]
* ```
*/
static filterInvalid<T extends Message>(messages: T[]): T[] {
return messages.filter(m => !m.isValid())
}
/**
* 过滤非空消息
*
* @param messages 消息数组
* @returns 只包含非空消息的数组
*
* 使用示例:
* ```typescript
* const people = [person1, emptyPerson, person3]
* const nonEmptyPeople = MessageUtils.filterNonEmpty(people)
* // nonEmptyPeople: [person1, person3]
* ```
*/
static filterNonEmpty<T extends Message>(messages: T[]): T[] {
return messages.filter(m => !m.isEmpty())
}
// ========== 查找操作 ==========
/**
* 查找第一个无效消息
*
* @param messages 消息数组
* @returns 第一个无效消息,如果都有效则返回 null
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* const invalid = MessageUtils.findInvalid(people)
* if (invalid !== null) {
* console.error('Found invalid message:', invalid.validate())
* }
* ```
*/
static findInvalid<T extends Message>(messages: T[]): T | null {
const found = messages.find(m => !m.isValid())
return found !== undefined ? found : null
}
/**
* 查找第一个有效消息
*
* @param messages 消息数组
* @returns 第一个有效消息,如果都无效则返回 null
*
* 使用示例:
* ```typescript
* const people = [invalidPerson1, validPerson, invalidPerson2]
* const valid = MessageUtils.findValid(people)
* ```
*/
static findValid<T extends Message>(messages: T[]): T | null {
const found = messages.find(m => m.isValid())
return found !== undefined ? found : null
}
/**
* 查找第一个空消息
*
* @param messages 消息数组
* @returns 第一个空消息,如果都非空则返回 null
*
* 使用示例:
* ```typescript
* const people = [person1, emptyPerson, person3]
* const empty = MessageUtils.findEmpty(people)
* ```
*/
static findEmpty<T extends Message>(messages: T[]): T | null {
const found = messages.find(m => m.isEmpty())
return found !== undefined ? found : null
}
// ========== 判断操作 ==========
/**
* 判断所有消息是否都有效
*
* @param messages 消息数组
* @returns true 如果所有消息都有效
*
* 使用示例:
* ```typescript
* const people = [person1, person2, person3]
* if (MessageUtils.allValid(people)) {
* console.log('All messages are valid')
* }
* ```
*/
static allValid<T extends Message>(messages: T[]): boolean {
return messages.every(m => m.isValid())
}
/**
* 判断是否存在无效消息
*
* @param messages 消息数组
* @returns true 如果存在至少一个无效消息
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* if (MessageUtils.hasInvalid(people)) {
* console.warn('Found invalid messages')
* }
* ```
*/
static hasInvalid<T extends Message>(messages: T[]): boolean {
return messages.some(m => !m.isValid())
}
/**
* 判断所有消息是否都为空
*
* @param messages 消息数组
* @returns true 如果所有消息都为空
*
* 使用示例:
* ```typescript
* const people = [emptyPerson1, emptyPerson2]
* if (MessageUtils.allEmpty(people)) {
* console.log('All messages are empty')
* }
* ```
*/
static allEmpty<T extends Message>(messages: T[]): boolean {
return messages.every(m => m.isEmpty())
}
/**
* 判断是否存在非空消息
*
* @param messages 消息数组
* @returns true 如果存在至少一个非空消息
*
* 使用示例:
* ```typescript
* const people = [emptyPerson, person1, emptyPerson2]
* if (MessageUtils.hasNonEmpty(people)) {
* console.log('Found non-empty messages')
* }
* ```
*/
static hasNonEmpty<T extends Message>(messages: T[]): boolean {
return messages.some(m => !m.isEmpty())
}
// ========== 分组操作 ==========
// groupBy 和 countBy 被移除,因为 ArkTS 不支持动态索引访问和 Keyof 泛型索引
// ========== 统计操作 ==========
/**
* 统计有效消息数量
*
* @param messages 消息数组
* @returns 有效消息的数量
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* const validCount = MessageUtils.countValid(people)
* // validCount: 2
* ```
*/
static countValid<T extends Message>(messages: T[]): number {
return messages.filter(m => m.isValid()).length
}
/**
* 统计无效消息数量
*
* @param messages 消息数组
* @returns 无效消息的数量
*
* 使用示例:
* ```typescript
* const people = [person1, invalidPerson, person3]
* const invalidCount = MessageUtils.countInvalid(people)
* // invalidCount: 1
* ```
*/
static countInvalid<T extends Message>(messages: T[]): number {
return messages.filter(m => !m.isValid()).length
}
/**
* 统计空消息数量
*
* @param messages 消息数组
* @returns 空消息的数量
*
* 使用示例:
* ```typescript
* const people = [person1, emptyPerson, person3]
* const emptyCount = MessageUtils.countEmpty(people)
* // emptyCount: 1
* ```
*/
static countEmpty<T extends Message>(messages: T[]): number {
return messages.filter(m => m.isEmpty()).length
}
/**
* 统计非空消息数量
*
* @param messages 消息数组
* @returns 非空消息的数量
*
* 使用示例:
* ```typescript
* const people = [person1, emptyPerson, person3]
* const nonEmptyCount = MessageUtils.countNonEmpty(people)
* // nonEmptyCount: 2
* ```
*/
static countNonEmpty<T extends Message>(messages: T[]): number {
return messages.filter(m => !m.isEmpty()).length
}
// ========== 转换操作 ==========
// pluck 被移除,因为 ArkTS 不支持动态索引访问
/**
* 去重(基于二进制比较)
*
* @param messages 消息数组
* @returns 去重后的消息数组
*
* 使用示例:
* ```typescript
* const people = [person1, person2, person1Clone, person3]
* const uniquePeople = MessageUtils.unique(people)
* // uniquePeople: [person1, person2, person3]
* ```
*/
static unique<T extends Message>(messages: T[]): T[] {
const seen = new Set<string>()
const result: T[] = []
for (const msg of messages) {
// 使用二进制数据作为唯一标识
try {
const binary = msg.toBinary(true)
const key = MessageUtils.binaryToString(binary)
if (!seen.has(key)) {
seen.add(key)
result.push(msg)
}
} catch (e) {
// 如果序列化失败,直接添加(避免丢失数据)
result.push(msg)
}
}
return result
}
/**
* 辅助方法:将 Uint8Array 转换为字符串(用于 Set 比较)
*/
private static binaryToString(binary: Uint8Array): string {
let result = ''
for (let i = 0; i < binary.length; i++) {
result += String.fromCharCode(binary[i])
}
return result
}
}