summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-03-27 15:03:11 -0500
committerJeff Carr <[email protected]>2025-03-27 15:03:11 -0500
commit4b7f42004512c83869370c20cc22398b1a4a2ab2 (patch)
treed86665981e1e56ae597a4775ef614bbbc5638e59
parentbe94cbe32077eeee49969adffc169f879f153ab1 (diff)
a real world example
-rw-r--r--argv.go3
-rw-r--r--example/Signal-Desktop-Backups.proto1315
-rw-r--r--main.go5
-rw-r--r--protoReformat.go146
4 files changed, 1405 insertions, 64 deletions
diff --git a/argv.go b/argv.go
index 0f248cb..9b05d60 100644
--- a/argv.go
+++ b/argv.go
@@ -19,7 +19,8 @@ type args struct {
Regret bool `arg:"--regret" help:"ignore needed UUID. You will eventually regret this."`
Delete bool `arg:"--delete" help:"use delete with copy experiment"`
DryRun bool `arg:"--dry-run" help:"check the .proto syntax, but don't do anything"`
- Format bool `arg:"--format" help:"eformat the .proto file and exit"`
+ Format bool `arg:"--format" help:"format the .proto file and exit"`
+ Comments bool `arg:"--format-comments" help:"enforce parseable comments in a .proto file"`
NoFormat bool `arg:"--no-format" help:"do not auto-reformat the .proto file"`
GoSrc string `arg:"--go-src" help:"default is ~/go/src. could be set to your go.work path"`
GoPath string `arg:"--gopath" help:"the gopath of this repo"`
diff --git a/example/Signal-Desktop-Backups.proto b/example/Signal-Desktop-Backups.proto
new file mode 100644
index 0000000..c939e53
--- /dev/null
+++ b/example/Signal-Desktop-Backups.proto
@@ -0,0 +1,1315 @@
+// Copyright 2024 Signal Messenger, LLC
+// SPDX-License-Identifier: AGPL-3.0-only
+syntax = "proto3";
+
+package signalbackups;
+
+option java_package = "org.thoughtcrime.securesms.backup.v2.proto";
+
+message BackupInfo {
+ uint64 version = 1;
+ uint64 backupTimeMs = 2;
+ bytes mediaRootBackupKey = 3; // 32-byte random value generated when the backup is uploaded for the first time.
+ string currentAppVersion = 4;
+ string firstAppVersion = 5;
+}
+
+// Frames must follow in the following ordering rules:
+//
+// 1. There is exactly one AccountData and it is the first frame.
+// 2. A frame referenced by ID must come before the referencing frame.
+// e.g. a Recipient must come before any Chat referencing it.
+// 3. All ChatItems must appear in global Chat rendering order.
+// (The order in which they were received by the client.)
+// 4. ChatFolders must appear in render order (e.g., left to right for
+// LTR locales), but can appear anywhere relative to other frames respecting
+// rule 2 (after Recipients and Chats).
+//
+// Recipients, Chats, StickerPacks, AdHocCalls, and NotificationProfiles
+// can be in any order. (But must respect rule 2.)
+//
+// For example, Chats may all be together at the beginning,
+// or may each immediately precede its first ChatItem.
+message Frame {
+ // If unset, importers should skip this frame without throwing an error.
+ oneof item {
+ AccountData account = 1;
+ Recipient recipient = 2;
+ Chat chat = 3;
+ ChatItem chatItem = 4;
+ StickerPack stickerPack = 5;
+ AdHocCall adHocCall = 6;
+ NotificationProfile notificationProfile = 7;
+ ChatFolder chatFolder = 8;
+ }
+}
+
+message AccountData {
+ enum PhoneNumberSharingMode {
+ UNKNOWN = 0; // Interpret as "Nobody"
+ EVERYBODY = 1;
+ NOBODY = 2;
+ }
+ message UsernameLink {
+ enum Color {
+ UNKNOWN = 0; // Interpret as "Blue"
+ BLUE = 1;
+ WHITE = 2;
+ GREY = 3;
+ OLIVE = 4;
+ GREEN = 5;
+ ORANGE = 6;
+ PINK = 7;
+ PURPLE = 8;
+ }
+
+ bytes entropy = 1; // 32 bytes of entropy used for encryption
+ bytes serverId = 2; // 16 bytes of encoded UUID provided by the server
+ Color color = 3;
+ }
+
+ message AccountSettings {
+ bool readReceipts = 1;
+ bool sealedSenderIndicators = 2;
+ bool typingIndicators = 3;
+ bool linkPreviews = 4;
+ bool notDiscoverableByPhoneNumber = 5;
+ bool preferContactAvatars = 6;
+ uint32 universalExpireTimerSeconds = 7; // 0 means no universal expire timer.
+ repeated string preferredReactionEmoji = 8;
+ bool displayBadgesOnProfile = 9;
+ bool keepMutedChatsArchived = 10;
+ bool hasSetMyStoriesPrivacy = 11;
+ bool hasViewedOnboardingStory = 12;
+ bool storiesDisabled = 13;
+ optional bool storyViewReceiptsEnabled = 14;
+ bool hasSeenGroupStoryEducationSheet = 15;
+ bool hasCompletedUsernameOnboarding = 16;
+ PhoneNumberSharingMode phoneNumberSharingMode = 17;
+ ChatStyle defaultChatStyle = 18;
+ repeated ChatStyle.CustomChatColor customChatColors = 19;
+ }
+
+ message SubscriberData {
+ bytes subscriberId = 1;
+ string currencyCode = 2;
+ bool manuallyCancelled = 3;
+ }
+
+ message IAPSubscriberData {
+ bytes subscriberId = 1;
+
+ // If unset, importers should ignore the subscriber data without throwing an error.
+ oneof iapSubscriptionId {
+ // Identifies an Android Play Store IAP subscription.
+ string purchaseToken = 2;
+ // Identifies an iOS App Store IAP subscription.
+ uint64 originalTransactionId = 3;
+ }
+ }
+
+ bytes profileKey = 1;
+ optional string username = 2;
+ UsernameLink usernameLink = 3;
+ string givenName = 4;
+ string familyName = 5;
+ string avatarUrlPath = 6;
+ SubscriberData donationSubscriberData = 7;
+ reserved 8; // A deprecated format // backupsSubscriberData
+ AccountSettings accountSettings = 9;
+ IAPSubscriberData backupsSubscriberData = 10;
+ string svrPin = 11;
+}
+
+message Recipient {
+ uint64 id = 1; // generated id for reference only within this file
+ // If unset, importers should skip this frame without throwing an error.
+ oneof destination {
+ Contact contact = 2;
+ Group group = 3;
+ DistributionListItem distributionList = 4;
+ Self self = 5;
+ ReleaseNotes releaseNotes = 6;
+ CallLink callLink = 7;
+ }
+}
+
+// If unset - computed as the value of the first byte of SHA-256(msg=CONTACT_ID)
+// modulo the count of colors. Once set the avatar color for a recipient is
+// never recomputed or changed.
+//
+// `CONTACT_ID` is the first available identifier from the list:
+// - ServiceIdToBinary(ACI)
+// - E164
+// - ServiceIdToBinary(PNI)
+// - Group Id
+enum AvatarColor {
+ A100 = 0;
+ A110 = 1;
+ A120 = 2;
+ A130 = 3;
+ A140 = 4;
+ A150 = 5;
+ A160 = 6;
+ A170 = 7;
+ A180 = 8;
+ A190 = 9;
+ A200 = 10;
+ A210 = 11;
+}
+
+message Contact {
+ enum IdentityState {
+ DEFAULT = 0; // A valid value -- indicates unset by the user
+ VERIFIED = 1;
+ UNVERIFIED = 2; // Was once verified and is now unverified
+ }
+
+ message Registered {}
+ message NotRegistered {
+ uint64 unregisteredTimestamp = 1;
+ }
+
+ enum Visibility {
+ VISIBLE = 0; // A valid value -- the contact is not hidden
+ HIDDEN = 1;
+ HIDDEN_MESSAGE_REQUEST = 2;
+ }
+
+ message Name {
+ string given = 1;
+ string family = 2;
+ }
+
+ optional bytes aci = 1; // should be 16 bytes
+ optional bytes pni = 2; // should be 16 bytes
+ optional string username = 3;
+ optional uint64 e164 = 4;
+ bool blocked = 5;
+ Visibility visibility = 6;
+
+ // If unset, consider the user to be registered
+ oneof registration {
+ Registered registered = 7;
+ NotRegistered notRegistered = 8;
+ }
+
+ optional bytes profileKey = 9;
+ bool profileSharing = 10;
+ optional string profileGivenName = 11;
+ optional string profileFamilyName = 12;
+ bool hideStory = 13;
+ optional bytes identityKey = 14;
+ IdentityState identityState = 15;
+ Name nickname = 16; // absent iff both `given` and `family` are empty
+ string note = 17;
+ string systemGivenName = 18;
+ string systemFamilyName = 19;
+ string systemNickname = 20;
+ optional AvatarColor avatarColor = 21;
+}
+
+message Group {
+ enum StorySendMode {
+ DEFAULT = 0; // A valid value -- indicates unset by the user
+ DISABLED = 1;
+ ENABLED = 2;
+ }
+
+ bytes masterKey = 1;
+ bool whitelisted = 2;
+ bool hideStory = 3;
+ StorySendMode storySendMode = 4;
+ GroupSnapshot snapshot = 5;
+ bool blocked = 6;
+ optional AvatarColor avatarColor = 7;
+
+ // These are simply plaintext copies of the groups proto from Groups.proto.
+ // They should be kept completely in-sync with Groups.proto.
+ // These exist to allow us to have the latest snapshot of a group during restoration without having to hit the network.
+ // We would use Groups.proto if we could, but we want a plaintext version to improve export readability.
+ // For documentation, defer to Groups.proto. The only name change is Group -> GroupSnapshot to avoid the naming conflict.
+ message GroupSnapshot {
+ reserved 1; // The field is deprecated in the context of static group state // publicKey
+ GroupAttributeBlob title = 2;
+ GroupAttributeBlob description = 11;
+ string avatarUrl = 3;
+ GroupAttributeBlob disappearingMessagesTimer = 4;
+ AccessControl accessControl = 5;
+ uint32 version = 6;
+ repeated Member members = 7;
+ repeated MemberPendingProfileKey membersPendingProfileKey = 8;
+ repeated MemberPendingAdminApproval membersPendingAdminApproval = 9;
+ bytes inviteLinkPassword = 10;
+ bool announcements_only = 12;
+ repeated MemberBanned members_banned = 13;
+ }
+
+ message GroupAttributeBlob {
+ // If unset, consider the field it represents to not be present
+ oneof content {
+ string title = 1;
+ bytes avatar = 2;
+ uint32 disappearingMessagesDuration = 3;
+ string descriptionText = 4;
+ }
+ }
+
+ message Member {
+ enum Role {
+ UNKNOWN = 0; // Intepret as "Default"
+ DEFAULT = 1;
+ ADMINISTRATOR = 2;
+ }
+
+ bytes userId = 1;
+ Role role = 2;
+ reserved 3; // This field is ignored in Backups, in favor of Contact frames for members // profileKey
+ reserved 4; // This field is deprecated in the context of static group state // presentation
+ uint32 joinedAtVersion = 5;
+ }
+
+ message MemberPendingProfileKey {
+ Member member = 1;
+ bytes addedByUserId = 2;
+ uint64 timestamp = 3;
+ }
+
+ message MemberPendingAdminApproval {
+ bytes userId = 1;
+ reserved 2; // This field is ignored in Backups, in favor of Contact frames for members // profileKey
+ reserved 3; // This field is deprecated in the context of static group state // presentation
+ uint64 timestamp = 4;
+ }
+
+ message MemberBanned {
+ bytes userId = 1;
+ uint64 timestamp = 2;
+ }
+
+ message AccessControl {
+ enum AccessRequired {
+ UNKNOWN = 0; // Intepret as "Unsatisfiable"
+ ANY = 1;
+ MEMBER = 2;
+ ADMINISTRATOR = 3;
+ UNSATISFIABLE = 4;
+ }
+
+ AccessRequired attributes = 1;
+ AccessRequired members = 2;
+ AccessRequired addFromInviteLink = 3;
+ }
+}
+
+message Self {
+ optional AvatarColor avatarColor = 1;
+}
+
+message ReleaseNotes {}
+
+message Chat {
+ uint64 id = 1; // generated id for reference only within this file
+ uint64 recipientId = 2;
+ bool archived = 3;
+ optional uint32 pinnedOrder = 4; // will be displayed in ascending order
+ optional uint64 expirationTimerMs = 5;
+ optional uint64 muteUntilMs = 6; // INT64_MAX (2^63 - 1) = "always muted".
+ bool markedUnread = 7;
+ bool dontNotifyForMentionsIfMuted = 8;
+ ChatStyle style = 9;
+ uint32 expireTimerVersion = 10;
+}
+
+/**
+ * Call Links have some associated data including a call, but unlike other recipients
+ * are not tied to threads because they do not have messages associated with them.
+ *
+ * note:
+ * - room id can be derived from the root key
+ * - the presence of an admin key means this user is a call admin
+ */
+message CallLink {
+ enum Restrictions {
+ UNKNOWN = 0; // Interpret as "Admin Approval"
+ NONE = 1;
+ ADMIN_APPROVAL = 2;
+ }
+
+ bytes rootKey = 1;
+ optional bytes adminKey = 2; // Only present if the user is an admin
+ string name = 3;
+ Restrictions restrictions = 4;
+ uint64 expirationMs = 5;
+}
+
+message AdHocCall {
+ enum State {
+ UNKNOWN_STATE = 0; // Interpret as "Generic"
+ GENERIC = 1;
+ }
+
+ uint64 callId = 1;
+ // Refers to a `CallLink` recipient.
+ uint64 recipientId = 2;
+ State state = 3;
+ uint64 callTimestamp = 4;
+}
+
+message DistributionListItem {
+ // distribution ids are UUIDv4s. "My Story" is represented
+ // by an all-0 UUID (00000000-0000-0000-0000-000000000000).
+ bytes distributionId = 1; // distribution list ids are uuids
+
+ // If unset, importers should skip the item entirely without showing an error.
+ oneof item {
+ uint64 deletionTimestamp = 2;
+ DistributionList distributionList = 3;
+ }
+}
+
+message DistributionList {
+ enum PrivacyMode {
+ UNKNOWN = 0; // Interpret as "Only with"
+ ONLY_WITH = 1;
+ ALL_EXCEPT = 2;
+ ALL = 3;
+ }
+
+ string name = 1;
+ bool allowReplies = 2;
+ PrivacyMode privacyMode = 3;
+ repeated uint64 memberRecipientIds = 4; // generated recipient id
+}
+
+message ChatItem {
+ message IncomingMessageDetails {
+ uint64 dateReceived = 1;
+ optional uint64 dateServerSent = 2;
+ bool read = 3;
+ bool sealedSender = 4;
+ }
+
+ message OutgoingMessageDetails {
+ repeated SendStatus sendStatus = 1;
+ }
+
+ message DirectionlessMessageDetails {
+ }
+
+ uint64 chatId = 1; // conversation id
+ uint64 authorId = 2; // recipient id
+ uint64 dateSent = 3;
+ optional uint64 expireStartDate = 4; // timestamp of when expiration timer started ticking down
+ optional uint64 expiresInMs = 5; // how long timer of message is (ms)
+ repeated ChatItem revisions = 6; // ordered from oldest to newest
+ bool sms = 7;
+
+ // If unset, importers should skip this item without throwing an error.
+ oneof directionalDetails {
+ IncomingMessageDetails incoming = 8;
+ OutgoingMessageDetails outgoing = 9;
+ DirectionlessMessageDetails directionless = 10;
+ }
+
+ // If unset, importers should skip this item without throwing an error.
+ oneof item {
+ StandardMessage standardMessage = 11;
+ ContactMessage contactMessage = 12;
+ StickerMessage stickerMessage = 13;
+ RemoteDeletedMessage remoteDeletedMessage = 14;
+ ChatUpdateMessage updateMessage = 15;
+ PaymentNotification paymentNotification = 16;
+ GiftBadge giftBadge = 17;
+ ViewOnceMessage viewOnceMessage = 18;
+ DirectStoryReplyMessage directStoryReplyMessage = 19; // group story reply messages are not backed up
+ }
+}
+
+message SendStatus {
+ message Pending {}
+
+ message Sent {
+ bool sealedSender = 1;
+ }
+
+ message Delivered {
+ bool sealedSender = 1;
+ }
+
+ message Read {
+ bool sealedSender = 1;
+ }
+
+ message Viewed {
+ bool sealedSender = 1;
+ }
+
+ // e.g. user in group was blocked, so we skipped sending to them
+ message Skipped {}
+
+ message Failed {
+ enum FailureReason {
+ UNKNOWN = 0; // A valid value -- could indicate a crash or lack of information
+ NETWORK = 1;
+ IDENTITY_KEY_MISMATCH = 2;
+ }
+
+ FailureReason reason = 1;
+ }
+
+ uint64 recipientId = 1;
+ uint64 timestamp = 2; // the time the status was last updated -- if from a receipt, it should be the sentTime of the receipt
+
+ // If unset, importers should consider the status to be "pending"
+ oneof deliveryStatus {
+ Pending pending = 3;
+ Sent sent = 4;
+ Delivered delivered = 5;
+ Read read = 6;
+ Viewed viewed = 7;
+ Skipped skipped = 8;
+ Failed failed = 9;
+ }
+}
+
+message Text {
+ string body = 1;
+ repeated BodyRange bodyRanges = 2;
+}
+
+message StandardMessage {
+ optional Quote quote = 1;
+ optional Text text = 2;
+ repeated MessageAttachment attachments = 3;
+ repeated LinkPreview linkPreview = 4;
+ optional FilePointer longText = 5;
+ repeated Reaction reactions = 6;
+}
+
+message ContactMessage {
+ ContactAttachment contact = 1;
+ repeated Reaction reactions = 2;
+}
+
+message DirectStoryReplyMessage {
+ message TextReply {
+ Text text = 1;
+ FilePointer longText = 2;
+ }
+
+ // If unset, importers should ignore the message without throwing an error.
+ oneof reply {
+ TextReply textReply = 1;
+ string emoji = 2;
+ }
+
+ repeated Reaction reactions = 3;
+ reserved 4; // storySentTimestamp
+}
+
+message PaymentNotification {
+ message TransactionDetails {
+ message MobileCoinTxoIdentification { // Used to map to payments on the ledger
+ repeated bytes publicKey = 1; // for received transactions
+ repeated bytes keyImages = 2; // for sent transactions
+ }
+
+ message FailedTransaction { // Failed payments can't be synced from the ledger
+ enum FailureReason {
+ GENERIC = 0; // A valid value -- reason unknown
+ NETWORK = 1;
+ INSUFFICIENT_FUNDS = 2;
+ }
+ FailureReason reason = 1;
+ }
+
+ message Transaction {
+ enum Status {
+ INITIAL = 0; // A valid value -- state unconfirmed
+ SUBMITTED = 1;
+ SUCCESSFUL = 2;
+ }
+ Status status = 1;
+
+ // This identification is used to map the payment table to the ledger
+ // and is likely required otherwise we may have issues reconciling with
+ // the ledger
+ MobileCoinTxoIdentification mobileCoinIdentification = 2;
+ optional uint64 timestamp = 3;
+ optional uint64 blockIndex = 4;
+ optional uint64 blockTimestamp = 5;
+ optional bytes transaction = 6; // mobile coin blobs
+ optional bytes receipt = 7; // mobile coin blobs
+ }
+
+ // If unset, importers should treat the transaction as successful with no metadata.
+ oneof payment {
+ Transaction transaction = 1;
+ FailedTransaction failedTransaction = 2;
+ }
+ }
+
+ optional string amountMob = 1; // stored as a decimal string, e.g. 1.00001
+ optional string feeMob = 2; // stored as a decimal string, e.g. 1.00001
+ optional string note = 3;
+ TransactionDetails transactionDetails = 4;
+}
+
+message GiftBadge {
+ enum State {
+ UNOPENED = 0; // A valid state
+ OPENED = 1;
+ REDEEMED = 2;
+ FAILED = 3;
+ }
+
+ bytes receiptCredentialPresentation = 1;
+ State state = 2;
+}
+
+message ViewOnceMessage {
+ // Will be null for viewed messages
+ MessageAttachment attachment = 1;
+ repeated Reaction reactions = 2;
+}
+
+message ContactAttachment {
+ message Name {
+ string givenName = 1;
+ string familyName = 2;
+ string prefix = 3;
+ string suffix = 4;
+ string middleName = 5;
+ string nickname = 6;
+ }
+
+ message Phone {
+ enum Type {
+ UNKNOWN = 0; // Interpret as "Home"
+ HOME = 1;
+ MOBILE = 2;
+ WORK = 3;
+ CUSTOM = 4;
+ }
+
+ string value = 1;
+ Type type = 2;
+ string label = 3;
+ }
+
+ message Email {
+ enum Type {
+ UNKNOWN = 0; // Intepret as "Home"
+ HOME = 1;
+ MOBILE = 2;
+ WORK = 3;
+ CUSTOM = 4;
+ }
+
+ string value = 1;
+ Type type = 2;
+ string label = 3;
+ }
+
+ message PostalAddress {
+ enum Type {
+ UNKNOWN = 0; // Interpret as "Home"
+ HOME = 1;
+ WORK = 2;
+ CUSTOM = 3;
+ }
+
+ Type type = 1;
+ string label = 2;
+ string street = 3;
+ string pobox = 4;
+ string neighborhood = 5;
+ string city = 6;
+ string region = 7;
+ string postcode = 8;
+ string country = 9;
+ }
+
+ optional Name name = 1;
+ repeated Phone number = 3;
+ repeated Email email = 4;
+ repeated PostalAddress address = 5;
+ optional FilePointer avatar = 6;
+ string organization = 7;
+}
+
+message StickerMessage {
+ Sticker sticker = 1;
+ repeated Reaction reactions = 2;
+}
+
+// Tombstone for remote delete
+message RemoteDeletedMessage {}
+
+message Sticker {
+ bytes packId = 1;
+ bytes packKey = 2;
+ uint32 stickerId = 3;
+ optional string emoji = 4;
+ // Stickers are uploaded to be sent as attachments; we also
+ // back them up as normal attachments when they are in messages.
+ // DO NOT treat this as the definitive source of a sticker in
+ // an installed StickerPack that shares the same packId.
+ FilePointer data = 5;
+}
+
+message LinkPreview {
+ string url = 1;
+ optional string title = 2;
+ optional FilePointer image = 3;
+ optional string description = 4;
+ optional uint64 date = 5;
+}
+
+// A FilePointer on a message that has additional
+// metadata that applies only to message attachments.
+message MessageAttachment {
+ // Similar to SignalService.AttachmentPointer.Flags,
+ // but explicitly mutually exclusive. Note the different raw values
+ // (non-zero starting values are not supported in proto3.)
+ enum Flag {
+ NONE = 0; // A valid value -- no flag applied
+ VOICE_MESSAGE = 1;
+ BORDERLESS = 2;
+ GIF = 3;
+ }
+
+ FilePointer pointer = 1;
+ Flag flag = 2;
+ bool wasDownloaded = 3;
+ // Cross-client identifier for this attachment among all attachments on the
+ // owning message. See: SignalService.AttachmentPointer.clientUuid.
+ optional bytes clientUuid = 4;
+}
+
+message FilePointer {
+ // References attachments in the backup (media) storage tier.
+ message BackupLocator {
+ string mediaName = 1;
+ // If present, the cdn number of the succesful upload.
+ // If empty/0, may still have been uploaded, and clients
+ // can discover the cdn number via the list endpoint.
+ optional uint32 cdnNumber = 2;
+ bytes key = 3;
+ bytes digest = 4;
+ uint32 size = 5;
+ // Fallback in case backup tier upload failed.
+ optional string transitCdnKey = 6;
+ optional uint32 transitCdnNumber = 7;
+ }
+
+ // References attachments in the transit storage tier.
+ // May be downloaded or not when the backup is generated;
+ // primarily for free-tier users who cannot copy the
+ // attachments to the backup (media) storage tier.
+ message AttachmentLocator {
+ string cdnKey = 1;
+ uint32 cdnNumber = 2;
+ optional uint64 uploadTimestamp = 3;
+ bytes key = 4;
+ bytes digest = 5;
+ uint32 size = 6;
+ }
+
+ // References attachments that are invalid in such a way where download
+ // cannot be attempted. Could range from missing digests to missing
+ // CDN keys or anything else that makes download attempts impossible.
+ // This serves as a 'tombstone' so that the UX can show that an attachment
+ // did exist, but for whatever reason it's not retrievable.
+ message InvalidAttachmentLocator {
+ }
+
+ // If unset, importers should consider it to be an InvalidAttachmentLocator without throwing an error.
+ oneof locator {
+ BackupLocator backupLocator = 1;
+ AttachmentLocator attachmentLocator = 2;
+ InvalidAttachmentLocator invalidAttachmentLocator = 3;
+ }
+
+ optional string contentType = 4;
+ optional bytes incrementalMac = 5;
+ optional uint32 incrementalMacChunkSize = 6;
+ optional string fileName = 7;
+ optional uint32 width = 8;
+ optional uint32 height = 9;
+ optional string caption = 10;
+ optional string blurHash = 11;
+}
+
+message Quote {
+ enum Type {
+ UNKNOWN = 0; // Interpret as "Normal"
+ NORMAL = 1;
+ GIFT_BADGE = 2;
+ VIEW_ONCE = 3;
+ }
+
+ message QuotedAttachment {
+ optional string contentType = 1;
+ optional string fileName = 2;
+ optional MessageAttachment thumbnail = 3;
+ }
+
+ optional uint64 targetSentTimestamp = 1; // null if the target message could not be found at time of quote insert
+ uint64 authorId = 2;
+ optional Text text = 3;
+ repeated QuotedAttachment attachments = 4;
+ Type type = 5;
+}
+
+message BodyRange {
+ enum Style {
+ NONE = 0; // Importers should ignore the body range without throwing an error.
+ BOLD = 1;
+ ITALIC = 2;
+ SPOILER = 3;
+ STRIKETHROUGH = 4;
+ MONOSPACE = 5;
+ }
+
+ // 'start' and 'length' are measured in UTF-16 code units.
+ // They may refer to offsets in a longText attachment.
+ uint32 start = 1;
+ uint32 length = 2;
+
+ // If unset, importers should ignore the body range without throwing an error.
+ oneof associatedValue {
+ bytes mentionAci = 3;
+ Style style = 4;
+ }
+}
+
+message Reaction {
+ string emoji = 1;
+ uint64 authorId = 2;
+ uint64 sentTimestamp = 3;
+ // A higher sort order means that a reaction is more recent. Some clients may export this as
+ // incrementing numbers (e.g. 1, 2, 3), others as timestamps.
+ uint64 sortOrder = 4;
+}
+
+message ChatUpdateMessage {
+ // If unset, importers should ignore the update message without throwing an error.
+ oneof update {
+ SimpleChatUpdate simpleUpdate = 1;
+ GroupChangeChatUpdate groupChange = 2;
+ ExpirationTimerChatUpdate expirationTimerChange = 3;
+ ProfileChangeChatUpdate profileChange = 4;
+ ThreadMergeChatUpdate threadMerge = 5;
+ SessionSwitchoverChatUpdate sessionSwitchover = 6;
+ IndividualCall individualCall = 7;
+ GroupCall groupCall = 8;
+ LearnedProfileChatUpdate learnedProfileChange = 9;
+ }
+}
+
+message IndividualCall {
+ enum Type {
+ UNKNOWN_TYPE = 0; // Interpret as "Audio call"
+ AUDIO_CALL = 1;
+ VIDEO_CALL = 2;
+ }
+
+ enum Direction {
+ UNKNOWN_DIRECTION = 0; // Interpret as "Incoming"
+ INCOMING = 1;
+ OUTGOING = 2;
+ }
+
+ enum State {
+ UNKNOWN_STATE = 0; // Interpret as "Accepted"
+ ACCEPTED = 1;
+ NOT_ACCEPTED = 2;
+ // An incoming call that is no longer ongoing, which we neither accepted
+ // not actively declined. For example, it expired, was canceled by the
+ // sender, or was rejected due to being in another call.
+ MISSED = 3;
+ // We auto-declined an incoming call due to a notification profile.
+ MISSED_NOTIFICATION_PROFILE = 4;
+ }
+
+ optional uint64 callId = 1;
+ Type type = 2;
+ Direction direction = 3;
+ State state = 4;
+ uint64 startedCallTimestamp = 5;
+ bool read = 6;
+}
+
+message GroupCall {
+ enum State {
+ UNKNOWN_STATE = 0; // Interpret as "Generic"
+ // A group call was started without ringing.
+ GENERIC = 1;
+ // We joined a group call that was started without ringing.
+ JOINED = 2;
+ // An incoming group call is actively ringing.
+ RINGING = 3;
+ // We accepted an incoming group ring.
+ ACCEPTED = 4;
+ // We declined an incoming group ring.
+ DECLINED = 5;
+ // We missed an incoming group ring, for example because it expired.
+ MISSED = 6;
+ // We auto-declined an incoming group ring due to a notification profile.
+ MISSED_NOTIFICATION_PROFILE = 7;
+ // An outgoing ring was started. We don't track any state for outgoing rings
+ // beyond that they started.
+ OUTGOING_RING = 8;
+ }
+
+ optional uint64 callId = 1;
+ State state = 2;
+ optional uint64 ringerRecipientId = 3;
+ optional uint64 startedCallRecipientId = 4;
+ uint64 startedCallTimestamp = 5;
+ optional uint64 endedCallTimestamp = 6; // The time the call ended.
+ bool read = 7;
+}
+
+message SimpleChatUpdate {
+ enum Type {
+ UNKNOWN = 0; // Importers should skip the update without throwing an error.
+ JOINED_SIGNAL = 1;
+ IDENTITY_UPDATE = 2;
+ IDENTITY_VERIFIED = 3;
+ IDENTITY_DEFAULT = 4; // marking as unverified
+ CHANGE_NUMBER = 5;
+ RELEASE_CHANNEL_DONATION_REQUEST = 6;
+ END_SESSION = 7;
+ CHAT_SESSION_REFRESH = 8;
+ BAD_DECRYPT = 9;
+ PAYMENTS_ACTIVATED = 10;
+ PAYMENT_ACTIVATION_REQUEST = 11;
+ UNSUPPORTED_PROTOCOL_MESSAGE = 12;
+ REPORTED_SPAM = 13;
+ BLOCKED = 14;
+ UNBLOCKED = 15;
+ MESSAGE_REQUEST_ACCEPTED = 16;
+ }
+
+ Type type = 1;
+}
+
+// For 1:1 chat updates only.
+// For group thread updates use GroupExpirationTimerUpdate.
+message ExpirationTimerChatUpdate {
+ uint64 expiresInMs = 1; // 0 means the expiration timer was disabled
+}
+
+message ProfileChangeChatUpdate {
+ string previousName = 1;
+ string newName = 2;
+}
+
+message LearnedProfileChatUpdate {
+ // If unset, importers should consider the previous name to be an empty string.
+ oneof previousName {
+ uint64 e164 = 1;
+ string username = 2;
+ }
+}
+
+message ThreadMergeChatUpdate {
+ uint64 previousE164 = 1;
+}
+
+message SessionSwitchoverChatUpdate {
+ uint64 e164 = 1;
+}
+
+message GroupChangeChatUpdate {
+ message Update {
+ // If unset, importers should consider it to be a GenericGroupUpdate with unset updaterAci
+ oneof update {
+ GenericGroupUpdate genericGroupUpdate = 1;
+ GroupCreationUpdate groupCreationUpdate = 2;
+ GroupNameUpdate groupNameUpdate = 3;
+ GroupAvatarUpdate groupAvatarUpdate = 4;
+ GroupDescriptionUpdate groupDescriptionUpdate = 5;
+ GroupMembershipAccessLevelChangeUpdate groupMembershipAccessLevelChangeUpdate = 6;
+ GroupAttributesAccessLevelChangeUpdate groupAttributesAccessLevelChangeUpdate = 7;
+ GroupAnnouncementOnlyChangeUpdate groupAnnouncementOnlyChangeUpdate = 8;
+ GroupAdminStatusUpdate groupAdminStatusUpdate = 9;
+ GroupMemberLeftUpdate groupMemberLeftUpdate = 10;
+ GroupMemberRemovedUpdate groupMemberRemovedUpdate = 11;
+ SelfInvitedToGroupUpdate selfInvitedToGroupUpdate = 12;
+ SelfInvitedOtherUserToGroupUpdate selfInvitedOtherUserToGroupUpdate = 13;
+ GroupUnknownInviteeUpdate groupUnknownInviteeUpdate = 14;
+ GroupInvitationAcceptedUpdate groupInvitationAcceptedUpdate = 15;
+ GroupInvitationDeclinedUpdate groupInvitationDeclinedUpdate = 16;
+ GroupMemberJoinedUpdate groupMemberJoinedUpdate = 17;
+ GroupMemberAddedUpdate groupMemberAddedUpdate = 18;
+ GroupSelfInvitationRevokedUpdate groupSelfInvitationRevokedUpdate = 19;
+ GroupInvitationRevokedUpdate groupInvitationRevokedUpdate = 20;
+ GroupJoinRequestUpdate groupJoinRequestUpdate = 21;
+ GroupJoinRequestApprovalUpdate groupJoinRequestApprovalUpdate = 22;
+ GroupJoinRequestCanceledUpdate groupJoinRequestCanceledUpdate = 23;
+ GroupInviteLinkResetUpdate groupInviteLinkResetUpdate = 24;
+ GroupInviteLinkEnabledUpdate groupInviteLinkEnabledUpdate = 25;
+ GroupInviteLinkAdminApprovalUpdate groupInviteLinkAdminApprovalUpdate = 26;
+ GroupInviteLinkDisabledUpdate groupInviteLinkDisabledUpdate = 27;
+ GroupMemberJoinedByLinkUpdate groupMemberJoinedByLinkUpdate = 28;
+ GroupV2MigrationUpdate groupV2MigrationUpdate = 29;
+ GroupV2MigrationSelfInvitedUpdate groupV2MigrationSelfInvitedUpdate = 30;
+ GroupV2MigrationInvitedMembersUpdate groupV2MigrationInvitedMembersUpdate = 31;
+ GroupV2MigrationDroppedMembersUpdate groupV2MigrationDroppedMembersUpdate = 32;
+ GroupSequenceOfRequestsAndCancelsUpdate groupSequenceOfRequestsAndCancelsUpdate = 33;
+ GroupExpirationTimerUpdate groupExpirationTimerUpdate = 34;
+ }
+ }
+
+ // Must be one or more; all updates batched together came from
+ // a single batched group state update.
+ repeated Update updates = 1;
+}
+
+message GenericGroupUpdate {
+ optional bytes updaterAci = 1;
+}
+
+message GroupCreationUpdate {
+ optional bytes updaterAci = 1;
+}
+
+message GroupNameUpdate {
+ optional bytes updaterAci = 1;
+ // Null value means the group name was removed.
+ optional string newGroupName = 2;
+}
+
+message GroupAvatarUpdate {
+ optional bytes updaterAci = 1;
+ bool wasRemoved = 2;
+}
+
+message GroupDescriptionUpdate {
+ optional bytes updaterAci = 1;
+ // Null value means the group description was removed.
+ optional string newDescription = 2;
+}
+
+enum GroupV2AccessLevel {
+ UNKNOWN = 0; // Interpret as "Unsatisfiable"
+ ANY = 1;
+ MEMBER = 2;
+ ADMINISTRATOR = 3;
+ UNSATISFIABLE = 4;
+}
+
+message GroupMembershipAccessLevelChangeUpdate {
+ optional bytes updaterAci = 1;
+ GroupV2AccessLevel accessLevel = 2;
+}
+
+message GroupAttributesAccessLevelChangeUpdate {
+ optional bytes updaterAci = 1;
+ GroupV2AccessLevel accessLevel = 2;
+}
+
+message GroupAnnouncementOnlyChangeUpdate {
+ optional bytes updaterAci = 1;
+ bool isAnnouncementOnly = 2;
+}
+
+message GroupAdminStatusUpdate {
+ optional bytes updaterAci = 1;
+ // The aci who had admin status granted or revoked.
+ bytes memberAci = 2;
+ bool wasAdminStatusGranted = 3;
+}
+
+message GroupMemberLeftUpdate {
+ bytes aci = 1;
+}
+
+message GroupMemberRemovedUpdate {
+ optional bytes removerAci = 1;
+ bytes removedAci = 2;
+}
+
+message SelfInvitedToGroupUpdate {
+ optional bytes inviterAci = 1;
+}
+
+message SelfInvitedOtherUserToGroupUpdate {
+ // If no invitee id available, use GroupUnknownInviteeUpdate
+ bytes inviteeServiceId = 1;
+}
+
+message GroupUnknownInviteeUpdate {
+ // Can be the self user.
+ optional bytes inviterAci = 1;
+ uint32 inviteeCount = 2;
+}
+
+message GroupInvitationAcceptedUpdate {
+ optional bytes inviterAci = 1;
+ bytes newMemberAci = 2;
+}
+
+message GroupInvitationDeclinedUpdate {
+ optional bytes inviterAci = 1;
+ // Note: if invited by pni, just set inviteeAci to nil.
+ optional bytes inviteeAci = 2;
+}
+
+message GroupMemberJoinedUpdate {
+ bytes newMemberAci = 1;
+}
+
+message GroupMemberAddedUpdate {
+ optional bytes updaterAci = 1;
+ bytes newMemberAci = 2;
+ bool hadOpenInvitation = 3;
+ // If hadOpenInvitation is true, optionally include aci of the inviter.
+ optional bytes inviterAci = 4;
+}
+
+// An invitation to self was revoked.
+message GroupSelfInvitationRevokedUpdate {
+ optional bytes revokerAci = 1;
+}
+
+// These invitees should never be the local user.
+// Use GroupSelfInvitationRevokedUpdate in those cases.
+// The inviter or updater can be the local user.
+message GroupInvitationRevokedUpdate {
+ message Invitee {
+ optional bytes inviterAci = 1;
+ // Prefer to use aci over pni. No need to set
+ // pni if aci is set. Both can be missing.
+ optional bytes inviteeAci = 2;
+ optional bytes inviteePni = 3;
+ }
+
+ // The member that revoked the invite(s), not the inviter!
+ // Assumed to be an admin (at the time, may no longer be an
+ // admin or even a member).
+ optional bytes updaterAci = 1;
+ repeated Invitee invitees = 2;
+}
+
+message GroupJoinRequestUpdate {
+ bytes requestorAci = 1;
+}
+
+message GroupJoinRequestApprovalUpdate {
+ bytes requestorAci = 1;
+ // The aci that approved or rejected the request.
+ optional bytes updaterAci = 2;
+ bool wasApproved = 3;
+}
+
+message GroupJoinRequestCanceledUpdate {
+ bytes requestorAci = 1;
+}
+
+// A single requestor has requested to join and cancelled
+// their request repeatedly with no other updates in between.
+// The last action encompassed by this update is always a
+// cancellation; if there was another open request immediately
+// after, it will be a separate GroupJoinRequestUpdate, either
+// in the same frame or in a subsequent frame.
+message GroupSequenceOfRequestsAndCancelsUpdate {
+ bytes requestorAci = 1;
+ uint32 count = 2;
+}
+
+message GroupInviteLinkResetUpdate {
+ optional bytes updaterAci = 1;
+}
+
+message GroupInviteLinkEnabledUpdate {
+ optional bytes updaterAci = 1;
+ bool linkRequiresAdminApproval = 2;
+}
+
+message GroupInviteLinkAdminApprovalUpdate {
+ optional bytes updaterAci = 1;
+ bool linkRequiresAdminApproval = 2;
+}
+
+message GroupInviteLinkDisabledUpdate {
+ optional bytes updaterAci = 1;
+}
+
+message GroupMemberJoinedByLinkUpdate {
+ bytes newMemberAci = 1;
+}
+
+// A gv1->gv2 migration occurred.
+message GroupV2MigrationUpdate {}
+
+// Another user migrated gv1->gv2 but was unable to add
+// the local user and invited them instead.
+message GroupV2MigrationSelfInvitedUpdate {}
+
+// The local user migrated gv1->gv2 but was unable to
+// add some members and invited them instead.
+// (Happens if we don't have the invitee's profile key)
+message GroupV2MigrationInvitedMembersUpdate {
+ uint32 invitedMembersCount = 1;
+}
+
+// The local user migrated gv1->gv2 but was unable to
+// add or invite some members and dropped them instead.
+// (Happens for e164 members where we don't have an aci).
+message GroupV2MigrationDroppedMembersUpdate {
+ uint32 droppedMembersCount = 1;
+}
+
+// For 1:1 timer updates, use ExpirationTimerChatUpdate.
+message GroupExpirationTimerUpdate {
+ uint64 expiresInMs = 1; // 0 means the expiration timer was disabled
+ optional bytes updaterAci = 2;
+}
+
+message StickerPack {
+ bytes packId = 1;
+ bytes packKey = 2;
+}
+
+message ChatStyle {
+ message Gradient {
+ uint32 angle = 1; // degrees
+ repeated fixed32 colors = 2; // 0xAARRGGBB
+ repeated float positions = 3; // percent from 0 to 1
+ }
+
+ message CustomChatColor {
+ uint64 id = 1;
+
+ // If unset, use the default chat color
+ oneof color {
+ fixed32 solid = 2; // 0xAARRGGBB
+ Gradient gradient = 3;
+ }
+ }
+
+ message AutomaticBubbleColor {
+ }
+
+ enum WallpaperPreset {
+ UNKNOWN_WALLPAPER_PRESET = 0; // Interpret as the wallpaper being unset
+ SOLID_BLUSH = 1;
+ SOLID_COPPER = 2;
+ SOLID_DUST = 3;
+ SOLID_CELADON = 4;
+ SOLID_RAINFOREST = 5;
+ SOLID_PACIFIC = 6;
+ SOLID_FROST = 7;
+ SOLID_NAVY = 8;
+ SOLID_LILAC = 9;
+ SOLID_PINK = 10;
+ SOLID_EGGPLANT = 11;
+ SOLID_SILVER = 12;
+ GRADIENT_SUNSET = 13;
+ GRADIENT_NOIR = 14;
+ GRADIENT_HEATMAP = 15;
+ GRADIENT_AQUA = 16;
+ GRADIENT_IRIDESCENT = 17;
+ GRADIENT_MONSTERA = 18;
+ GRADIENT_BLISS = 19;
+ GRADIENT_SKY = 20;
+ GRADIENT_PEACH = 21;
+ }
+
+ enum BubbleColorPreset {
+ UNKNOWN_BUBBLE_COLOR_PRESET = 0; // Interpret as the user's default chat bubble color
+ SOLID_ULTRAMARINE = 1;
+ SOLID_CRIMSON = 2;
+ SOLID_VERMILION = 3;
+ SOLID_BURLAP = 4;
+ SOLID_FOREST = 5;
+ SOLID_WINTERGREEN = 6;
+ SOLID_TEAL = 7;
+ SOLID_BLUE = 8;
+ SOLID_INDIGO = 9;
+ SOLID_VIOLET = 10;
+ SOLID_PLUM = 11;
+ SOLID_TAUPE = 12;
+ SOLID_STEEL = 13;
+ GRADIENT_EMBER = 14;
+ GRADIENT_MIDNIGHT = 15;
+ GRADIENT_INFRARED = 16;
+ GRADIENT_LAGOON = 17;
+ GRADIENT_FLUORESCENT = 18;
+ GRADIENT_BASIL = 19;
+ GRADIENT_SUBLIME = 20;
+ GRADIENT_SEA = 21;
+ GRADIENT_TANGERINE = 22;
+ }
+
+ // If unset, importers should consider there to be no wallpaper.
+ oneof wallpaper {
+ WallpaperPreset wallpaperPreset = 1;
+ // This `FilePointer` is expected not to contain a `fileName`, `width`,
+ // `height`, or `caption`.
+ FilePointer wallpaperPhoto = 2;
+ }
+
+ // If unset, importers should consider it to be AutomaticBubbleColor
+ oneof bubbleColor {
+ // Bubble setting is automatically determined based on the wallpaper setting,
+ // or `SOLID_ULTRAMARINE` for `noWallpaper`
+ AutomaticBubbleColor autoBubbleColor = 3;
+ BubbleColorPreset bubbleColorPreset = 4;
+
+ // See AccountSettings.customChatColors
+ uint64 customColorId = 5;
+ }
+
+ bool dimWallpaperInDarkMode = 7;
+}
+
+message NotificationProfile {
+ enum DayOfWeek {
+ UNKNOWN = 0; // Interpret as "Monday"
+ MONDAY = 1;
+ TUESDAY = 2;
+ WEDNESDAY = 3;
+ THURSDAY = 4;
+ FRIDAY = 5;
+ SATURDAY = 6;
+ SUNDAY = 7;
+ }
+
+ string name = 1;
+ optional string emoji = 2;
+ fixed32 color = 3; // 0xAARRGGBB
+ uint64 createdAtMs = 4;
+ bool allowAllCalls = 5;
+ bool allowAllMentions = 6;
+ repeated uint64 allowedMembers = 7; // generated recipient id for allowed groups and contacts
+ bool scheduleEnabled = 8;
+ uint32 scheduleStartTime = 9; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
+ uint32 scheduleEndTime = 10; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345)
+ repeated DayOfWeek scheduleDaysEnabled = 11;
+}
+
+message ChatFolder {
+ // Represents the default "All chats" folder record vs all other custom folders
+ enum FolderType {
+ UNKNOWN = 0; // Interpret as "Custom"
+ ALL = 1;
+ CUSTOM = 2;
+ }
+
+ string name = 1;
+ bool showOnlyUnread = 2;
+ bool showMutedChats = 3;
+ // Folder includes all 1:1 chats, unless excluded
+ bool includeAllIndividualChats = 4;
+ // Folder includes all group chats, unless excluded
+ bool includeAllGroupChats = 5;
+ FolderType folderType = 6;
+ repeated uint64 includedRecipientIds = 7; // generated recipient id of groups, contacts, and/or note to self
+ repeated uint64 excludedRecipientIds = 8; // generated recipient id of groups, contacts, and/or note to self
+}
diff --git a/main.go b/main.go
index e4c510f..e0702f9 100644
--- a/main.go
+++ b/main.go
@@ -66,6 +66,10 @@ func main() {
protoReformat(argv.Proto)
okExit("")
}
+ if argv.Comments {
+ protoReformatComments(argv.Proto)
+ okExit("")
+ }
if argv.Regret {
// this will override the manditory Uuid checks
@@ -86,6 +90,7 @@ func main() {
log.Info("autogenpb parse error:", err)
badExit(err)
}
+
if !argv.NoFormat {
protoReformat(argv.Proto)
}
diff --git a/protoReformat.go b/protoReformat.go
index 07baf76..5cd685d 100644
--- a/protoReformat.go
+++ b/protoReformat.go
@@ -40,13 +40,12 @@ func (msg *StdMessage) name() string {
}
type Message interface {
- format() []string
name() string
load()
addMsg(Message)
}
-func protoReformat(filename string) error {
+func protoReformatComments(filename string) error {
// read in the .proto file
data, err := os.ReadFile(filename)
if err != nil {
@@ -56,7 +55,6 @@ func protoReformat(filename string) error {
var newfile string
- /* check the comment preprocessor
log.Info("filename", filename)
alltest := makeLineIter(data)
// gets the max vartype and varname
@@ -64,8 +62,18 @@ func protoReformat(filename string) error {
newfile += fmt.Sprintln(commentPreprocessor(line))
}
saveFile(filename, newfile)
- os.Exit(-1)
- */
+ return nil
+}
+
+func protoReformat(filename string) error {
+ // read in the .proto file
+ data, err := os.ReadFile(filename)
+ if err != nil {
+ log.Info("file read failed", filename, err)
+ return err
+ }
+
+ var newfile string
var fmtmsg *FormatMsg
fmtmsg = new(FormatMsg)
@@ -109,11 +117,12 @@ func protoReformat(filename string) error {
// write out the messages
allTheLines = newLinesScanner(strings.Split(string(data), "\n"))
for allTheLines.Scan() {
- line := allTheLines.Next()
+ line := allTheLines.NextRaw()
+
if strings.HasPrefix(line, "oneof ") {
newmsg := fmtmsg.newOneofMessage(line)
newmsg.load()
- for _, newline := range newmsg.format() {
+ for _, newline := range newmsg.msgPB.format() {
newfile += fmt.Sprintln(newline)
}
continue
@@ -123,7 +132,7 @@ func protoReformat(filename string) error {
newmsg := fmtmsg.newEnumMessage(line)
newmsg.load()
// loadEnumDefinition(newmsg)
- for _, newline := range newmsg.format() {
+ for _, newline := range newmsg.msgPB.format() {
newfile += fmt.Sprintln(newline)
}
continue
@@ -133,7 +142,7 @@ func protoReformat(filename string) error {
newmsg := fmtmsg.newStdMessage(line)
newmsg.load()
log.Info("got to message", line)
- for _, newline := range newmsg.format() {
+ for _, newline := range newmsg.msgPB.format() {
newfile += fmt.Sprintln(newline)
}
continue
@@ -215,13 +224,13 @@ func (msg *StdMessage) load() {
if strings.HasPrefix(line, "oneof ") {
newmsg := msg.msgPB.newOneofMessage(line)
newmsg.load()
- curPB = newmsg.msgPB
+ // curPB = newmsg.msgPB
continue
}
if strings.HasPrefix(line, "enum ") {
newmsg := msg.msgPB.newEnumMessage(line)
newmsg.load()
- curPB = newmsg.msgPB
+ // curPB = newmsg.msgPB
// loadEnumDefinition(newmsg)
continue
}
@@ -229,14 +238,14 @@ func (msg *StdMessage) load() {
// message inception. search for the architect. don't forget your totem
newmsg := msg.msgPB.newStdMessage(line)
newmsg.load()
- curPB = newmsg.msgPB
+ // curPB = newmsg.msgPB
continue
}
if strings.HasPrefix(line, "}") {
msg.msgPB.Footer = line
return
}
- curPB.Notes = append(curPB.Notes, line)
+ curPB.Lines = append(curPB.Lines, line)
// fmtmsg.Lines = append(fmtmsg.Lines, line)
}
@@ -319,36 +328,54 @@ func setMaxSizes(curmsg *FormatMsg) {
}
}
-func (curmsg *FormatMsg) format() []string {
- return formatEnum(curmsg)
+// use this for header and footer lines
+func (msg *FormatMsg) padBase() string {
+ var pad string
+ for i := 1; i < int(msg.Depth); i += 1 {
+ pad += fmt.Sprintf("%8s", " ")
+ }
+ return pad
}
-func (curmsg *EnumMessage) format() []string {
- return formatEnum(curmsg.msgPB)
+// use this for lines inside the message
+func (msg *FormatMsg) pad() string {
+ var pad string
+ for i := 0; i < int(msg.Depth); i += 1 {
+ pad += fmt.Sprintf("%8s", " ")
+ }
+ return pad
}
func formatEnum(curmsg *FormatMsg) []string {
var newmsg []string
- newmsg = append(newmsg, curmsg.Header) // +" //header")
+ header := fmt.Sprintf("%s%s // enum depth=%d", curmsg.padBase(), curmsg.Header, curmsg.Depth)
+ newmsg = append(newmsg, header)
for _, line := range curmsg.Lines {
- line = " " + strings.TrimSpace(line)
+ line = fmt.Sprintf("%s%s", curmsg.pad(), line)
newmsg = append(newmsg, line)
}
- newmsg = append(newmsg, curmsg.Footer) // +" //footer")
+ footer := fmt.Sprintf("%s%s // enum footer depth=%d", curmsg.padBase(), curmsg.Footer, curmsg.Depth)
+ newmsg = append(newmsg, footer)
return newmsg
}
-func (curmsg *StdMessage) format() []string {
- return formatMessage(curmsg.msgPB)
+func (msg *FormatMsg) format() []string {
+ switch msg.Type {
+ case FormatMsg_ENUM:
+ return formatEnum(msg)
+ case FormatMsg_MESSAGE:
+ return formatMessage(msg)
+ }
+ return formatMessage(msg)
}
func formatMessage(curmsg *FormatMsg) []string {
var newmsg []string
if curmsg.Header != "" {
- line := curmsg.Header
+ line := fmt.Sprintf("%s%s // msg depth=%d", curmsg.padBase(), curmsg.Header, curmsg.Depth)
parts := strings.Fields(line)
if len(parts) > 3 {
// hack to actually indent comments on the message line itself. you're welcome
@@ -356,51 +383,34 @@ func formatMessage(curmsg *FormatMsg) []string {
end := strings.Join(parts[3:], " ")
offset := int(curmsg.MaxVarname) + int(curmsg.MaxVartype) + 16 - len(start)
pad := fmt.Sprintf("%d", offset)
- hmm := "%s %" + pad + "s %s"
- line = fmt.Sprintf(hmm, start, " ", end)
+ hmm := "%s %" + pad + "s %s // depth=%d"
+ line = fmt.Sprintf(hmm, start, " ", end, curmsg.Depth)
+ } else {
+ line = fmt.Sprintf("%s // len(parts)=%d depth=%d", line, len(parts), curmsg.Depth)
}
- newmsg = append(newmsg, line) // +" //header")
+ newmsg = append(newmsg, line) // " //header")
+ } else {
+ newmsg = append(newmsg, "// ERROR: header was blank") // +" //header")
}
// find the max length of varname and vartype
setMaxSizes(curmsg)
- /*
- for _, line := range curmsg.Lines {
- parts := strings.Split(line, ";")
- if len(parts) < 2 {
- // line is blank or just a comment
- continue
- }
-
- vartype, varname, _, _ := tokenMsgVar(line)
- if len(vartype) > int(curmsg.MaxVartype) {
- curmsg.MaxVartype = int64(len(vartype))
- }
- if len(varname) > int(curmsg.MaxVarname) {
- curmsg.MaxVarname = int64(len(varname))
- }
- }
- */
-
- /*
- for _, msg := range curmsg.Enums {
- for _, newline := range formatEnum(msg) {
- newmsg = append(newmsg, newline)
- }
- }
- for _, msg := range curmsg.Oneofs {
- for _, newline := range formatEnum(msg) {
- newmsg = append(newmsg, newline)
+ for _, msg := range curmsg.Msgs {
+ switch msg.Type {
+ case FormatMsg_ENUM:
+ for _, line := range formatEnum(msg) {
+ line = fmt.Sprintf("%s%s", curmsg.pad(), line)
+ newmsg = append(newmsg, line)
}
- }
-
- for _, msg := range curmsg.Msgs {
- for _, newline := range msg.format() {
- newmsg = append(newmsg, newline)
+ case FormatMsg_MESSAGE:
+ for _, line := range formatMessage(msg) {
+ line = fmt.Sprintf("%s%s", curmsg.pad(), line)
+ newmsg = append(newmsg, line)
}
+ default:
}
- */
+ }
for _, line := range curmsg.Lines {
line = strings.TrimSpace(line)
@@ -455,7 +465,15 @@ func (it *LinesScanner) Scan() bool {
return true
}
-// Next() returns the next thing in the array
+// does no cleaning of the data
+func (it *LinesScanner) NextRaw() string {
+ if it.index-1 == len(it.things) {
+ fmt.Println("Next() error in LinesScanner", it.index)
+ }
+ return it.things[it.index-1]
+}
+
+// cleans out comments
func (it *LinesScanner) Next() string {
if it.index-1 == len(it.things) {
fmt.Println("Next() error in LinesScanner", it.index)
@@ -463,8 +481,8 @@ func (it *LinesScanner) Next() string {
// out := commentPreprocessor(it.things[it.index-1])
out := it.things[it.index-1]
out = commentPreprocessor(out)
- // return strings.TrimSpace(out)
- return out
+ return strings.TrimSpace(out)
+ // return out
}
// END DEFINE THE ITERATOR
@@ -481,11 +499,13 @@ func commentPreprocessor(line string) string {
var comments []string
for _, match := range matches {
comments = append(comments, strings.TrimSpace(match[1]))
+ // comments = append(comments, match[1])
}
// Remove the block comments from the original line
line = re.ReplaceAllString(line, "")
- line = strings.TrimSpace(line)
+ // line = strings.TrimSpace(line)
+ line = strings.TrimSuffix(line, " ")
// Append comments at the end with //
for _, comment := range comments {