storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/lifecycle/filter.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     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   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package lifecycle
    18  
    19  import (
    20  	"encoding/xml"
    21  	"io"
    22  )
    23  
    24  var (
    25  	errInvalidFilter = Errorf("Filter must have exactly one of Prefix, Tag, or And specified")
    26  )
    27  
    28  // Filter - a filter for a lifecycle configuration Rule.
    29  type Filter struct {
    30  	XMLName xml.Name `xml:"Filter"`
    31  	set     bool
    32  
    33  	Prefix Prefix
    34  
    35  	And    And
    36  	andSet bool
    37  
    38  	Tag    Tag
    39  	tagSet bool
    40  	// Caching tags, only once
    41  	cachedTags []string
    42  }
    43  
    44  // MarshalXML - produces the xml representation of the Filter struct
    45  // only one of Prefix, And and Tag should be present in the output.
    46  func (f Filter) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    47  	if err := e.EncodeToken(start); err != nil {
    48  		return err
    49  	}
    50  
    51  	switch {
    52  	case !f.And.isEmpty():
    53  		if err := e.EncodeElement(f.And, xml.StartElement{Name: xml.Name{Local: "And"}}); err != nil {
    54  			return err
    55  		}
    56  	case !f.Tag.IsEmpty():
    57  		if err := e.EncodeElement(f.Tag, xml.StartElement{Name: xml.Name{Local: "Tag"}}); err != nil {
    58  			return err
    59  		}
    60  	default:
    61  		// Always print Prefix field when both And & Tag are empty
    62  		if err := e.EncodeElement(f.Prefix, xml.StartElement{Name: xml.Name{Local: "Prefix"}}); err != nil {
    63  			return err
    64  		}
    65  	}
    66  
    67  	return e.EncodeToken(xml.EndElement{Name: start.Name})
    68  }
    69  
    70  // UnmarshalXML - decodes XML data.
    71  func (f *Filter) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
    72  	f.set = true
    73  	for {
    74  		// Read tokens from the XML document in a stream.
    75  		t, err := d.Token()
    76  		if err != nil {
    77  			if err == io.EOF {
    78  				break
    79  			}
    80  			return err
    81  		}
    82  
    83  		switch se := t.(type) {
    84  		case xml.StartElement:
    85  			switch se.Name.Local {
    86  			case "Prefix":
    87  				var p Prefix
    88  				if err = d.DecodeElement(&p, &se); err != nil {
    89  					return err
    90  				}
    91  				f.Prefix = p
    92  			case "And":
    93  				var and And
    94  				if err = d.DecodeElement(&and, &se); err != nil {
    95  					return err
    96  				}
    97  				f.And = and
    98  				f.andSet = true
    99  			case "Tag":
   100  				var tag Tag
   101  				if err = d.DecodeElement(&tag, &se); err != nil {
   102  					return err
   103  				}
   104  				f.Tag = tag
   105  				f.tagSet = true
   106  			default:
   107  				return errUnknownXMLTag
   108  			}
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  // IsEmpty returns true if Filter is not specified in the XML
   115  func (f Filter) IsEmpty() bool {
   116  	return !f.set
   117  }
   118  
   119  // Validate - validates the filter element
   120  func (f Filter) Validate() error {
   121  	if f.IsEmpty() {
   122  		return errXMLNotWellFormed
   123  	}
   124  	// A Filter must have exactly one of Prefix, Tag, or And specified.
   125  	if !f.And.isEmpty() {
   126  		if f.Prefix.set {
   127  			return errInvalidFilter
   128  		}
   129  		if !f.Tag.IsEmpty() {
   130  			return errInvalidFilter
   131  		}
   132  		if err := f.And.Validate(); err != nil {
   133  			return err
   134  		}
   135  	}
   136  	if f.Prefix.set {
   137  		if !f.Tag.IsEmpty() {
   138  			return errInvalidFilter
   139  		}
   140  	}
   141  	if !f.Tag.IsEmpty() {
   142  		if f.Prefix.set {
   143  			return errInvalidFilter
   144  		}
   145  		if err := f.Tag.Validate(); err != nil {
   146  			return err
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  // TestTags tests if the object tags satisfy the Filter tags requirement,
   153  // it returns true if there is no tags in the underlying Filter.
   154  func (f Filter) TestTags(tags []string) bool {
   155  	if f.cachedTags == nil {
   156  		tags := make([]string, 0)
   157  		for _, t := range append(f.And.Tags, f.Tag) {
   158  			if !t.IsEmpty() {
   159  				tags = append(tags, t.String())
   160  			}
   161  		}
   162  		f.cachedTags = tags
   163  	}
   164  	for _, ct := range f.cachedTags {
   165  		foundTag := false
   166  		for _, t := range tags {
   167  			if ct == t {
   168  				foundTag = true
   169  				break
   170  			}
   171  		}
   172  		if !foundTag {
   173  			return false
   174  		}
   175  	}
   176  	return true
   177  }