Skip to content

Commit 2467de0

Browse files
authored
Merge pull request #108 from arran4/issue89
Addition of some multiple Component Properties functionality and basis for later sanity checking
2 parents 7fb626c + bdf47c9 commit 2467de0

File tree

2 files changed

+172
-3
lines changed

2 files changed

+172
-3
lines changed

calendar.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,111 @@ const (
6464
ComponentPropertyTzid = ComponentProperty(PropertyTzid)
6565
ComponentPropertyComment = ComponentProperty(PropertyComment)
6666
ComponentPropertyRelatedTo = ComponentProperty(PropertyRelatedTo)
67+
ComponentPropertyMethod = ComponentProperty(PropertyMethod)
68+
ComponentPropertyRecurrenceId = ComponentProperty(PropertyRecurrenceId)
69+
ComponentPropertyDuration = ComponentProperty(PropertyDuration)
70+
ComponentPropertyContact = ComponentProperty(PropertyContact)
71+
ComponentPropertyRequestStatus = ComponentProperty(PropertyRequestStatus)
72+
ComponentPropertyRDate = ComponentProperty(PropertyRdate)
6773
)
6874

75+
// Required returns the rules from the RFC as to if they are required or not for any particular component type
76+
// If unspecified or incomplete, it returns false. -- This list is incomplete verify source. Happy to take PRs with reference
77+
// iana-prop and x-props are not covered as it would always be true and require an exhaustive list.
78+
func (cp ComponentProperty) Required(c Component) bool {
79+
// https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
80+
switch cp {
81+
case ComponentPropertyDtstamp, ComponentPropertyUniqueId:
82+
switch c.(type) {
83+
case *VEvent:
84+
return true
85+
}
86+
case ComponentPropertyDtStart:
87+
switch c := c.(type) {
88+
case *VEvent:
89+
return !c.HasProperty(ComponentPropertyMethod)
90+
}
91+
}
92+
return false
93+
}
94+
95+
// Exclusive returns the ComponentProperty's using the rules from the RFC as to if one or more existing properties are prohibiting this one
96+
// If unspecified or incomplete, it returns false. -- This list is incomplete verify source. Happy to take PRs with reference
97+
// iana-prop and x-props are not covered as it would always be true and require an exhaustive list.
98+
func (cp ComponentProperty) Exclusive(c Component) []ComponentProperty {
99+
// https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
100+
switch cp {
101+
case ComponentPropertyDtEnd:
102+
switch c := c.(type) {
103+
case *VEvent:
104+
if c.HasProperty(ComponentPropertyDuration) {
105+
return []ComponentProperty{ComponentPropertyDuration}
106+
}
107+
}
108+
case ComponentPropertyDuration:
109+
switch c := c.(type) {
110+
case *VEvent:
111+
if c.HasProperty(ComponentPropertyDtEnd) {
112+
return []ComponentProperty{ComponentPropertyDtEnd}
113+
}
114+
}
115+
}
116+
return nil
117+
}
118+
119+
// Singular returns the rules from the RFC as to if the spec states that if "Must not occur more than once"
120+
// iana-prop and x-props are not covered as it would always be true and require an exhaustive list.
121+
func (cp ComponentProperty) Singular(c Component) bool {
122+
// https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
123+
switch cp {
124+
case ComponentPropertyClass, ComponentPropertyCreated, ComponentPropertyDescription, ComponentPropertyGeo,
125+
ComponentPropertyLastModified, ComponentPropertyLocation, ComponentPropertyOrganizer, ComponentPropertyPriority,
126+
ComponentPropertySequence, ComponentPropertyStatus, ComponentPropertySummary, ComponentPropertyTransp,
127+
ComponentPropertyUrl, ComponentPropertyRecurrenceId:
128+
switch c.(type) {
129+
case *VEvent:
130+
return true
131+
}
132+
}
133+
return false
134+
}
135+
136+
// Optional returns the rules from the RFC as to if the spec states that if these are optional
137+
// iana-prop and x-props are not covered as it would always be true and require an exhaustive list.
138+
func (cp ComponentProperty) Optional(c Component) bool {
139+
// https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
140+
switch cp {
141+
case ComponentPropertyClass, ComponentPropertyCreated, ComponentPropertyDescription, ComponentPropertyGeo,
142+
ComponentPropertyLastModified, ComponentPropertyLocation, ComponentPropertyOrganizer, ComponentPropertyPriority,
143+
ComponentPropertySequence, ComponentPropertyStatus, ComponentPropertySummary, ComponentPropertyTransp,
144+
ComponentPropertyUrl, ComponentPropertyRecurrenceId, ComponentPropertyRrule, ComponentPropertyAttach,
145+
ComponentPropertyAttendee, ComponentPropertyCategories, ComponentPropertyComment,
146+
ComponentPropertyContact, ComponentPropertyExdate, ComponentPropertyRequestStatus, ComponentPropertyRelatedTo,
147+
ComponentPropertyResources, ComponentPropertyRDate:
148+
switch c.(type) {
149+
case *VEvent:
150+
return true
151+
}
152+
}
153+
return false
154+
}
155+
156+
// Multiple returns the rules from the RFC as to if the spec states explicitly if multiple are allowed
157+
// iana-prop and x-props are not covered as it would always be true and require an exhaustive list.
158+
func (cp ComponentProperty) Multiple(c Component) bool {
159+
// https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
160+
switch cp {
161+
case ComponentPropertyAttach, ComponentPropertyAttendee, ComponentPropertyCategories, ComponentPropertyComment,
162+
ComponentPropertyContact, ComponentPropertyExdate, ComponentPropertyRequestStatus, ComponentPropertyRelatedTo,
163+
ComponentPropertyResources, ComponentPropertyRDate:
164+
switch c.(type) {
165+
case *VEvent:
166+
return true
167+
}
168+
}
169+
return false
170+
}
171+
69172
type Property string
70173

71174
const (

components.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func NewComponent(uniqueId string) ComponentBase {
6262
}
6363
}
6464

65+
// GetProperty returns the first match for the particular property you're after. Please consider using:
66+
// ComponentProperty.Required to determine if GetProperty or GetProperties is more appropriate.
6567
func (cb *ComponentBase) GetProperty(componentProperty ComponentProperty) *IANAProperty {
6668
for i := range cb.Properties {
6769
if cb.Properties[i].IANAToken == string(componentProperty) {
@@ -71,6 +73,31 @@ func (cb *ComponentBase) GetProperty(componentProperty ComponentProperty) *IANAP
7173
return nil
7274
}
7375

76+
// GetProperties returns all matches for the particular property you're after. Please consider using:
77+
// ComponentProperty.Singular/ComponentProperty.Multiple to determine if GetProperty or GetProperties is more appropriate.
78+
func (cb *ComponentBase) GetProperties(componentProperty ComponentProperty) []*IANAProperty {
79+
var result []*IANAProperty
80+
for i := range cb.Properties {
81+
if cb.Properties[i].IANAToken == string(componentProperty) {
82+
result = append(result, &cb.Properties[i])
83+
}
84+
}
85+
return result
86+
}
87+
88+
// HasProperty returns true if a component property is in the component.
89+
func (cb *ComponentBase) HasProperty(componentProperty ComponentProperty) bool {
90+
for i := range cb.Properties {
91+
if cb.Properties[i].IANAToken == string(componentProperty) {
92+
return true
93+
}
94+
}
95+
return false
96+
}
97+
98+
// SetProperty replaces the first match for the particular property you're setting, otherwise adds it. Please consider using:
99+
// ComponentProperty.Singular/ComponentProperty.Multiple to determine if AddProperty, SetProperty or ReplaceProperty is
100+
// more appropriate.
74101
func (cb *ComponentBase) SetProperty(property ComponentProperty, value string, params ...PropertyParameter) {
75102
for i := range cb.Properties {
76103
if cb.Properties[i].IANAToken == string(property) {
@@ -86,6 +113,17 @@ func (cb *ComponentBase) SetProperty(property ComponentProperty, value string, p
86113
cb.AddProperty(property, value, params...)
87114
}
88115

116+
// ReplaceProperty replaces all matches of the particular property you're setting, otherwise adds it. Returns a slice
117+
// of removed properties. Please consider using:
118+
// ComponentProperty.Singular/ComponentProperty.Multiple to determine if AddProperty, SetProperty or ReplaceProperty is
119+
// more appropriate.
120+
func (cb *ComponentBase) ReplaceProperty(property ComponentProperty, value string, params ...PropertyParameter) []IANAProperty {
121+
removed := cb.RemoveProperty(property)
122+
cb.AddProperty(property, value, params...)
123+
return removed
124+
}
125+
126+
// AddProperty appends a property
89127
func (cb *ComponentBase) AddProperty(property ComponentProperty, value string, params ...PropertyParameter) {
90128
r := IANAProperty{
91129
BaseProperty{
@@ -101,16 +139,44 @@ func (cb *ComponentBase) AddProperty(property ComponentProperty, value string, p
101139
cb.Properties = append(cb.Properties, r)
102140
}
103141

104-
// RemoveProperty removes from the component all properties that has
105-
// the name passed in removeProp.
106-
func (cb *ComponentBase) RemoveProperty(removeProp ComponentProperty) {
142+
// RemoveProperty removes from the component all properties that is of a particular property type, returning an slice of
143+
// removed entities
144+
func (cb *ComponentBase) RemoveProperty(removeProp ComponentProperty) []IANAProperty {
107145
var keptProperties []IANAProperty
146+
var removedProperties []IANAProperty
108147
for i := range cb.Properties {
109148
if cb.Properties[i].IANAToken != string(removeProp) {
110149
keptProperties = append(keptProperties, cb.Properties[i])
150+
} else {
151+
removedProperties = append(removedProperties, cb.Properties[i])
152+
}
153+
}
154+
cb.Properties = keptProperties
155+
return removedProperties
156+
}
157+
158+
// RemovePropertyByValue removes from the component all properties that has a particular property type and value,
159+
// return a count of removed properties
160+
func (cb *ComponentBase) RemovePropertyByValue(removeProp ComponentProperty, value string) []IANAProperty {
161+
return cb.RemovePropertyByFunc(removeProp, func(p IANAProperty) bool {
162+
return p.Value == value
163+
})
164+
}
165+
166+
// RemovePropertyByFunc removes from the component all properties that has a particular property type and the function
167+
// remove returns true for
168+
func (cb *ComponentBase) RemovePropertyByFunc(removeProp ComponentProperty, remove func(p IANAProperty) bool) []IANAProperty {
169+
var keptProperties []IANAProperty
170+
var removedProperties []IANAProperty
171+
for i := range cb.Properties {
172+
if cb.Properties[i].IANAToken != string(removeProp) && remove(cb.Properties[i]) {
173+
keptProperties = append(keptProperties, cb.Properties[i])
174+
} else {
175+
removedProperties = append(removedProperties, cb.Properties[i])
111176
}
112177
}
113178
cb.Properties = keptProperties
179+
return removedProperties
114180
}
115181

116182
const (

0 commit comments

Comments
 (0)