package object import ( "errors" "fmt" "strconv" ) // SysAttributePrefix is a prefix of key to system attribute. const SysAttributePrefix = "__SYSTEM__" const ( // SysAttributeUploadID marks smaller parts of a split bigger object. SysAttributeUploadID = SysAttributePrefix + "UPLOAD_ID" // SysAttributeExpEpoch tells GC to delete object after that epoch. SysAttributeExpEpoch = SysAttributePrefix + "EXPIRATION_EPOCH" // SysAttributeTickEpoch defines what epoch must produce object // notification. SysAttributeTickEpoch = SysAttributePrefix + "TICK_EPOCH" // SysAttributeTickTopic defines what topic object notification // must be sent to. SysAttributeTickTopic = SysAttributePrefix + "TICK_TOPIC" ) // SysAttributePrefixNeoFS is a prefix of key to system attribute. // Deprecated: use SysAttributePrefix. const SysAttributePrefixNeoFS = "__NEOFS__" const ( // SysAttributeUploadIDNeoFS marks smaller parts of a split bigger object. // Deprecated: use SysAttributeUploadID. SysAttributeUploadIDNeoFS = SysAttributePrefixNeoFS + "UPLOAD_ID" // SysAttributeExpEpochNeoFS tells GC to delete object after that epoch. // Deprecated: use SysAttributeExpEpoch. SysAttributeExpEpochNeoFS = SysAttributePrefixNeoFS + "EXPIRATION_EPOCH" // SysAttributeTickEpochNeoFS defines what epoch must produce object // notification. // Deprecated: use SysAttributeTickEpoch. SysAttributeTickEpochNeoFS = SysAttributePrefixNeoFS + "TICK_EPOCH" // SysAttributeTickTopicNeoFS defines what topic object notification // must be sent to. // Deprecated: use SysAttributeTickTopic. SysAttributeTickTopicNeoFS = SysAttributePrefixNeoFS + "TICK_TOPIC" ) // NotificationInfo groups information about object notification // that can be written to object. // // Topic is an optional field. type NotificationInfo struct { epoch uint64 topic string } // Epoch returns object notification tick // epoch. func (n NotificationInfo) Epoch() uint64 { return n.epoch } // SetEpoch sets object notification tick // epoch. func (n *NotificationInfo) SetEpoch(epoch uint64) { n.epoch = epoch } // Topic return optional object notification // topic. func (n NotificationInfo) Topic() string { return n.topic } // SetTopic sets optional object notification // topic. func (n *NotificationInfo) SetTopic(topic string) { n.topic = topic } // WriteNotificationInfo writes NotificationInfo to the Object via attributes. Object must not be nil. // // Existing notification attributes are expected to be key-unique, otherwise undefined behavior. func WriteNotificationInfo(o *Object, ni NotificationInfo) { h := o.GetHeader() if h == nil { h = new(Header) o.SetHeader(h) } var ( attrs = h.GetAttributes() epoch = strconv.FormatUint(ni.Epoch(), 10) topic = ni.Topic() changedEpoch bool changedTopic bool deleteIndex = -1 ) for i := range attrs { switch attrs[i].GetKey() { case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS: attrs[i].SetValue(epoch) changedEpoch = true case SysAttributeTickTopic, SysAttributeTickTopicNeoFS: changedTopic = true if topic == "" { deleteIndex = i break } attrs[i].SetValue(topic) } if changedEpoch && changedTopic { break } } if deleteIndex != -1 { // approach without allocation/waste // coping works since the attributes // order is not important attrs[deleteIndex] = attrs[len(attrs)-1] attrs = attrs[:len(attrs)-1] } if !changedEpoch { index := len(attrs) attrs = append(attrs, Attribute{}) attrs[index].SetKey(SysAttributeTickEpoch) attrs[index].SetValue(epoch) } if !changedTopic && topic != "" { index := len(attrs) attrs = append(attrs, Attribute{}) attrs[index].SetKey(SysAttributeTickTopic) attrs[index].SetValue(topic) } h.SetAttributes(attrs) } // ErrNotificationNotSet means that object does not have notification. var ErrNotificationNotSet = errors.New("notification for object is not set") // GetNotificationInfo looks for object notification attributes. Object must not be nil. // Returns ErrNotificationNotSet if no corresponding attributes // were found. // // Existing notification attributes are expected to be key-unique, otherwise undefined behavior. func GetNotificationInfo(o *Object) (*NotificationInfo, error) { var ( foundEpoch bool ni = new(NotificationInfo) ) for _, attr := range o.GetHeader().GetAttributes() { switch key := attr.GetKey(); key { case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS: epoch, err := strconv.ParseUint(attr.GetValue(), 10, 64) if err != nil { return nil, fmt.Errorf("could not parse epoch: %w", err) } ni.SetEpoch(epoch) foundEpoch = true case SysAttributeTickTopic, SysAttributeTickTopicNeoFS: ni.SetTopic(attr.GetValue()) } } if !foundEpoch { return nil, ErrNotificationNotSet } return ni, nil }