github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/message/message.go (about)

     1  package message
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"sync"
     7  )
     8  
     9  var closedchan = make(chan struct{})
    10  
    11  func init() {
    12  	close(closedchan)
    13  }
    14  
    15  // Payload is the Message's payload.
    16  type Payload []byte
    17  
    18  // Message is the basic transfer unit.
    19  // Messages are emitted by Publishers and received by Subscribers.
    20  type Message struct {
    21  	// UUID is a unique identifier of message.
    22  	//
    23  	// It is only used by Watermill for debugging.
    24  	// UUID can be empty.
    25  	UUID string
    26  
    27  	// Metadata contains the message metadata.
    28  	//
    29  	// Can be used to store data which doesn't require unmarshalling the entire payload.
    30  	// It is something similar to HTTP request's headers.
    31  	//
    32  	// Metadata is marshaled and will be saved to the PubSub.
    33  	Metadata Metadata
    34  
    35  	// Payload is the message's payload.
    36  	Payload Payload
    37  
    38  	// ack is closed, when acknowledge is received.
    39  	ack chan struct{}
    40  	// noACk is closed, when negative acknowledge is received.
    41  	noAck chan struct{}
    42  
    43  	ackMutex    sync.Mutex
    44  	ackSentType ackType
    45  
    46  	ctx context.Context
    47  }
    48  
    49  // NewMessage creates a new Message with given uuid and payload.
    50  func NewMessage(uuid string, payload Payload) *Message {
    51  	return &Message{
    52  		UUID:     uuid,
    53  		Metadata: make(map[string]string),
    54  		Payload:  payload,
    55  		ack:      make(chan struct{}),
    56  		noAck:    make(chan struct{}),
    57  	}
    58  }
    59  
    60  type ackType int
    61  
    62  const (
    63  	noAckSent ackType = iota
    64  	ack
    65  	nack
    66  )
    67  
    68  // Equals compare, that two messages are equal. Acks/Nacks are not compared.
    69  func (m *Message) Equals(toCompare *Message) bool {
    70  	if m.UUID != toCompare.UUID {
    71  		return false
    72  	}
    73  	if len(m.Metadata) != len(toCompare.Metadata) {
    74  		return false
    75  	}
    76  	for key, value := range m.Metadata {
    77  		if value != toCompare.Metadata[key] {
    78  			return false
    79  		}
    80  	}
    81  	return bytes.Equal(m.Payload, toCompare.Payload)
    82  }
    83  
    84  // Ack sends message's acknowledgement.
    85  //
    86  // Ack is not blocking.
    87  // Ack is idempotent.
    88  // False is returned, if Nack is already sent.
    89  func (m *Message) Ack() bool {
    90  	m.ackMutex.Lock()
    91  	defer m.ackMutex.Unlock()
    92  
    93  	if m.ackSentType == nack {
    94  		return false
    95  	}
    96  	if m.ackSentType != noAckSent {
    97  		return true
    98  	}
    99  
   100  	m.ackSentType = ack
   101  	if m.ack == nil {
   102  		m.ack = closedchan
   103  	} else {
   104  		close(m.ack)
   105  	}
   106  
   107  	return true
   108  }
   109  
   110  // Nack sends message's negative acknowledgement.
   111  //
   112  // Nack is not blocking.
   113  // Nack is idempotent.
   114  // False is returned, if Ack is already sent.
   115  func (m *Message) Nack() bool {
   116  	m.ackMutex.Lock()
   117  	defer m.ackMutex.Unlock()
   118  
   119  	if m.ackSentType == ack {
   120  		return false
   121  	}
   122  	if m.ackSentType != noAckSent {
   123  		return true
   124  	}
   125  
   126  	m.ackSentType = nack
   127  
   128  	if m.noAck == nil {
   129  		m.noAck = closedchan
   130  	} else {
   131  		close(m.noAck)
   132  	}
   133  
   134  	return true
   135  }
   136  
   137  // Acked returns channel which is closed when acknowledgement is sent.
   138  //
   139  // Usage:
   140  //
   141  //	select {
   142  //	case <-message.Acked():
   143  //		// ack received
   144  //	case <-message.Nacked():
   145  //		// nack received
   146  //	}
   147  func (m *Message) Acked() <-chan struct{} {
   148  	return m.ack
   149  }
   150  
   151  // Nacked returns channel which is closed when negative acknowledgement is sent.
   152  //
   153  // Usage:
   154  //
   155  //	select {
   156  //	case <-message.Acked():
   157  //		// ack received
   158  //	case <-message.Nacked():
   159  //		// nack received
   160  //	}
   161  func (m *Message) Nacked() <-chan struct{} {
   162  	return m.noAck
   163  }
   164  
   165  // Context returns the message's context. To change the context, use
   166  // SetContext.
   167  //
   168  // The returned context is always non-nil; it defaults to the
   169  // background context.
   170  func (m *Message) Context() context.Context {
   171  	if m.ctx != nil {
   172  		return m.ctx
   173  	}
   174  	return context.Background()
   175  }
   176  
   177  // SetContext sets provided context to the message.
   178  func (m *Message) SetContext(ctx context.Context) {
   179  	m.ctx = ctx
   180  }
   181  
   182  // Copy copies all message without Acks/Nacks.
   183  // The context is not propagated to the copy.
   184  func (m *Message) Copy() *Message {
   185  	msg := NewMessage(m.UUID, m.Payload)
   186  	for k, v := range m.Metadata {
   187  		msg.Metadata.Set(k, v)
   188  	}
   189  	return msg
   190  }