178 lines
4.0 KiB
Go
178 lines
4.0 KiB
Go
|
package properties
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/dolanor/caldav-go/utils"
|
||
|
"log"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
var _ = log.Print
|
||
|
|
||
|
var propNameSanitizer = strings.NewReplacer(
|
||
|
"_", "-",
|
||
|
":", "\\:",
|
||
|
)
|
||
|
|
||
|
var propValueSanitizer = strings.NewReplacer(
|
||
|
"\"", "'",
|
||
|
"\\", "\\\\",
|
||
|
"\n", "\\n",
|
||
|
)
|
||
|
|
||
|
var propNameDesanitizer = strings.NewReplacer(
|
||
|
"-", "_",
|
||
|
"\\:", ":",
|
||
|
)
|
||
|
|
||
|
var propValueDesanitizer = strings.NewReplacer(
|
||
|
"'", "\"",
|
||
|
"\\\\", "\\",
|
||
|
"\\n", "\n",
|
||
|
)
|
||
|
|
||
|
type Property struct {
|
||
|
Name PropertyName
|
||
|
Value, DefaultValue string
|
||
|
Params Params
|
||
|
OmitEmpty, Required bool
|
||
|
}
|
||
|
|
||
|
func (p *Property) HasNameAndValue() bool {
|
||
|
return p.Name != "" && p.Value != ""
|
||
|
}
|
||
|
|
||
|
func (p *Property) Merge(override *Property) {
|
||
|
if override.Name != "" {
|
||
|
p.Name = override.Name
|
||
|
}
|
||
|
if override.Value != "" {
|
||
|
p.Value = override.Value
|
||
|
}
|
||
|
if override.Params != nil {
|
||
|
p.Params = override.Params
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func PropertyFromStructField(fs reflect.StructField) (p *Property) {
|
||
|
|
||
|
ftag := fs.Tag.Get("ical")
|
||
|
if fs.PkgPath != "" || ftag == "-" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
p = new(Property)
|
||
|
|
||
|
// parse the field tag
|
||
|
if ftag != "" {
|
||
|
tags := strings.Split(ftag, ",")
|
||
|
p.Name = PropertyName(tags[0])
|
||
|
if len(tags) > 1 {
|
||
|
if tags[1] == "omitempty" {
|
||
|
p.OmitEmpty = true
|
||
|
} else if tags[1] == "required" {
|
||
|
p.Required = true
|
||
|
} else {
|
||
|
p.DefaultValue = tags[1]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// make sure we have a name
|
||
|
if p.Name == "" {
|
||
|
p.Name = PropertyName(fs.Name)
|
||
|
}
|
||
|
|
||
|
p.Name = PropertyName(strings.ToUpper(string(p.Name)))
|
||
|
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
func MarshalProperty(p *Property) string {
|
||
|
name := strings.ToUpper(propNameSanitizer.Replace(string(p.Name)))
|
||
|
value := propValueSanitizer.Replace(p.Value)
|
||
|
keys := []string{name}
|
||
|
for name, value := range p.Params {
|
||
|
name = ParameterName(strings.ToUpper(propNameSanitizer.Replace(string(name))))
|
||
|
value = propValueSanitizer.Replace(value)
|
||
|
if strings.ContainsAny(value, " :") {
|
||
|
keys = append(keys, fmt.Sprintf("%s=\"%s\"", name, value))
|
||
|
} else {
|
||
|
keys = append(keys, fmt.Sprintf("%s=%s", name, value))
|
||
|
}
|
||
|
}
|
||
|
name = strings.Join(keys, ";")
|
||
|
return fmt.Sprintf("%s:%s", name, value)
|
||
|
}
|
||
|
|
||
|
func PropertyFromInterface(target interface{}) (p *Property, err error) {
|
||
|
|
||
|
var ierr error
|
||
|
if va, ok := target.(CanValidateValue); ok {
|
||
|
if ierr = va.ValidateICalValue(); ierr != nil {
|
||
|
err = utils.NewError(PropertyFromInterface, "interface failed validation", target, ierr)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p = new(Property)
|
||
|
|
||
|
if enc, ok := target.(CanEncodeName); ok {
|
||
|
if p.Name, ierr = enc.EncodeICalName(); ierr != nil {
|
||
|
err = utils.NewError(PropertyFromInterface, "interface failed name encoding", target, ierr)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if enc, ok := target.(CanEncodeParams); ok {
|
||
|
if p.Params, ierr = enc.EncodeICalParams(); ierr != nil {
|
||
|
err = utils.NewError(PropertyFromInterface, "interface failed params encoding", target, ierr)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if enc, ok := target.(CanEncodeValue); ok {
|
||
|
if p.Value, ierr = enc.EncodeICalValue(); ierr != nil {
|
||
|
err = utils.NewError(PropertyFromInterface, "interface failed value encoding", target, ierr)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
func UnmarshalProperty(line string) *Property {
|
||
|
nvp := strings.SplitN(line, ":", 2)
|
||
|
prop := new(Property)
|
||
|
if len(nvp) > 1 {
|
||
|
prop.Value = strings.TrimSpace(nvp[1])
|
||
|
}
|
||
|
npp := strings.Split(nvp[0], ";")
|
||
|
if len(npp) > 1 {
|
||
|
prop.Params = make(map[ParameterName]string, 0)
|
||
|
for i := 1; i < len(npp); i++ {
|
||
|
var key, value string
|
||
|
kvp := strings.Split(npp[i], "=")
|
||
|
key = strings.TrimSpace(kvp[0])
|
||
|
key = propNameDesanitizer.Replace(key)
|
||
|
if len(kvp) > 1 {
|
||
|
value = strings.TrimSpace(kvp[1])
|
||
|
value = propValueDesanitizer.Replace(value)
|
||
|
value = strings.Trim(value, "\"")
|
||
|
}
|
||
|
prop.Params[ParameterName(key)] = value
|
||
|
}
|
||
|
}
|
||
|
prop.Name = PropertyName(strings.TrimSpace(npp[0]))
|
||
|
prop.Name = PropertyName(propNameDesanitizer.Replace(string(prop.Name)))
|
||
|
prop.Value = propValueDesanitizer.Replace(prop.Value)
|
||
|
return prop
|
||
|
}
|
||
|
|
||
|
func NewProperty(name, value string) *Property {
|
||
|
return &Property{Name: PropertyName(name), Value: value}
|
||
|
}
|