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 }