Define struct and interface for corso paths (#339)
* Define struct and interface for corso paths * Add wrapper for ExchangeMail as an example
This commit is contained in:
parent
532922f662
commit
da8ff3c267
104
src/internal/path/exchange_path.go
Normal file
104
src/internal/path/exchange_path.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
emailCategory = "email"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Path = &ExchangeMail{}
|
||||||
|
|
||||||
|
type ExchangeMail struct {
|
||||||
|
Base
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExchangeEmailPath creates and returns a new ExchangeEmailPath struct after
|
||||||
|
// verifying the path is properly escaped and contains information for the
|
||||||
|
// required segments.
|
||||||
|
func NewExchangeMail(
|
||||||
|
tenant string,
|
||||||
|
user string,
|
||||||
|
folder []string,
|
||||||
|
item string,
|
||||||
|
) (*ExchangeMail, error) {
|
||||||
|
tmpFolder := strings.Join(folder, "")
|
||||||
|
if err := validateExchangeMailSegments(tenant, user, tmpFolder, item); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := newPath([][]string{
|
||||||
|
{tenant},
|
||||||
|
{emailCategory},
|
||||||
|
{user},
|
||||||
|
folder,
|
||||||
|
{item},
|
||||||
|
})
|
||||||
|
|
||||||
|
return &ExchangeMail{p}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExchangeMailFromEscapedSegments(tenant, user, folder, item string) (*ExchangeMail, error) {
|
||||||
|
if err := validateExchangeMailSegments(tenant, user, folder, item); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := newPathFromEscapedSegments([]string{tenant, emailCategory, user, folder, item})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ExchangeMail{p}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateExchangeMailSegments(tenant, user, folder, item string) error {
|
||||||
|
if len(tenant) == 0 {
|
||||||
|
return errors.Wrap(errMissingSegment, "tenant")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(user) == 0 {
|
||||||
|
return errors.Wrap(errMissingSegment, "user")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(folder) == 0 {
|
||||||
|
return errors.Wrap(errMissingSegment, "mail folder")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(item) == 0 {
|
||||||
|
return errors.Wrap(errMissingSegment, "mail item")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tenant returns the tenant ID for the referenced email resource.
|
||||||
|
func (emp ExchangeMail) Tenant() string {
|
||||||
|
return emp.segment(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cateory returns an identifier noting this is a path for an email resource.
|
||||||
|
func (emp ExchangeMail) Category() string {
|
||||||
|
return emp.segment(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User returns the user ID for the referenced email resource.
|
||||||
|
func (emp ExchangeMail) User() string {
|
||||||
|
return emp.segment(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folder returns the folder segment for the referenced email resource.
|
||||||
|
func (emp ExchangeMail) Folder() string {
|
||||||
|
return emp.segment(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emp ExchangeMail) FolderElements() []string {
|
||||||
|
return emp.unescapedSegmentElements(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mail returns the email ID for the referenced email resource.
|
||||||
|
func (emp ExchangeMail) Item() string {
|
||||||
|
return emp.segment(4)
|
||||||
|
}
|
||||||
98
src/internal/path/path.go
Normal file
98
src/internal/path/path.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Package path provides a set of functions for wrangling paths from the outside
|
||||||
|
// world into paths that corso can understand. Paths use the standard Unix path
|
||||||
|
// separator character '/'. If for some reason an individual element in a raw
|
||||||
|
// path contains the '/' character, it should be escaped with '\'. If the path
|
||||||
|
// contains '\' it should be escaped by turning it into '\\'.
|
||||||
|
//
|
||||||
|
// Paths can be split into elements by splitting on '/' if the '/' is not
|
||||||
|
// escaped. Additionally, corso may operate on segments in a path. Segments are
|
||||||
|
// made up of one or more path elements.
|
||||||
|
//
|
||||||
|
// Examples of paths splitting by elements and canonicalization with escaping:
|
||||||
|
// 1.
|
||||||
|
// input path: `this/is/a/path`
|
||||||
|
// elements of path: `this`, `is`, `a`, `path`
|
||||||
|
// 2.
|
||||||
|
// input path: `this/is\/a/path`
|
||||||
|
// elements of path: `this`, `is/a`, `path`
|
||||||
|
// 3.
|
||||||
|
// input path: `this/is\\/a/path`
|
||||||
|
// elements of path: `this`, `is\`, `a`, `path`
|
||||||
|
// 4.
|
||||||
|
// input path: `this/is\\\/a/path`
|
||||||
|
// elements of path: `this`, `is\/a`, `path`
|
||||||
|
// 5.
|
||||||
|
// input path: `this/is//a/path`
|
||||||
|
// elements of path: `this`, `is`, `a`, `path`
|
||||||
|
// 6.
|
||||||
|
// input path: `this/is\//a/path`
|
||||||
|
// elements of path: `this`, `is/`, `a`, `path`
|
||||||
|
// 7.
|
||||||
|
// input path: `this/is/a/path/`
|
||||||
|
// elements of path: `this`, `is`, `a`, `path`
|
||||||
|
// 8.
|
||||||
|
// input path: `this/is/a/path\/`
|
||||||
|
// elements of path: `this`, `is`, `a`, `path/`
|
||||||
|
|
||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errMissingSegment = errors.New("missing required path segment")
|
||||||
|
|
||||||
|
// TODO(ashmrtn): Getting the category should either be through type-switches or
|
||||||
|
// through a function, but if it's a function it should re-use existing enums
|
||||||
|
// for resource types.
|
||||||
|
// For now, adding generic functions to pull information from segments.
|
||||||
|
// Resources that don't have the requested information should return an empty
|
||||||
|
// string.
|
||||||
|
type Path interface {
|
||||||
|
String() string
|
||||||
|
Tenant() string
|
||||||
|
User() string
|
||||||
|
Folder() string
|
||||||
|
Item() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPath takes a path that is broken into segments and elements in the segment
|
||||||
|
// and returns a Base. Each element in the input is escaped.
|
||||||
|
func newPath(segments [][]string) Base {
|
||||||
|
return Base{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPathFromEscapedSegments takes already escaped segments of a path, verifies
|
||||||
|
// the segments are escaped properly, and returns a new Base struct. If there is
|
||||||
|
// an unescaped trailing '/' it is removed.
|
||||||
|
func newPathFromEscapedSegments(segments []string) (Base, error) {
|
||||||
|
return Base{}, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string that contains all path segments joined
|
||||||
|
// together. Elements of the path that need escaping will be escaped.
|
||||||
|
func (p Base) String() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// segment returns the nth segment of the path. Path segment indices are
|
||||||
|
// 0-based.
|
||||||
|
func (p Base) segment(n int) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// unescapedSegmentElements returns the unescaped version of the elements that
|
||||||
|
// comprise the requested segment.
|
||||||
|
func (p Base) unescapedSegmentElements(n int) []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformedSegments returns a slice of the path segments where each segments
|
||||||
|
// has also been transformed such that it contains no characters outside the set
|
||||||
|
// of acceptable file system path characters.
|
||||||
|
func (p Base) TransformedSegments() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user