diff --git a/api/filesys/filesystem.go b/api/filesys/filesystem.go index 2d8caf2b8..e5034092b 100644 --- a/api/filesys/filesystem.go +++ b/api/filesys/filesystem.go @@ -28,6 +28,8 @@ type FileSystem interface { Open(path string) (File, error) // IsDir returns true if the path is a directory. IsDir(path string) bool + // ReadDir returns a list of files and directories within a directory. + ReadDir(path string) ([]string, error) // CleanedAbs converts the given path into a // directory and a file name, where the directory // is represented as a ConfirmedDir and all that implies. diff --git a/api/filesys/fsnode.go b/api/filesys/fsnode.go index 2593e705c..13b0a2298 100644 --- a/api/filesys/fsnode.go +++ b/api/filesys/fsnode.go @@ -349,6 +349,29 @@ func (n *fsNode) IsDir(path string) bool { return result.isNodeADir() } +// ReadDir implements FileSystem. +func (n *fsNode) ReadDir(path string) ([]string, error) { + if !n.IsDir(path) { + return nil, fmt.Errorf("%s is not a directory", path) + } + + dir, err := n.Find(path) + if err != nil { + return nil, err + } + if dir == nil { + return nil, fmt.Errorf("could not find directory %s", path) + } + + keys := make([]string, len(dir.dir)) + i := 0 + for k := range dir.dir { + keys[i] = k + i++ + } + return keys, nil +} + // Size returns the size of the node. func (n *fsNode) Size() int64 { if n.isNodeADir() { diff --git a/api/filesys/fsondisk.go b/api/filesys/fsondisk.go index 6becbb6f1..86cd26d75 100644 --- a/api/filesys/fsondisk.go +++ b/api/filesys/fsondisk.go @@ -100,6 +100,19 @@ func (fsOnDisk) IsDir(name string) bool { return info.IsDir() } +// ReadDir delegates to os.ReadDir +func (fsOnDisk) ReadDir(name string) ([]string, error) { + dirEntries, err := os.ReadDir(name) + if err != nil { + return nil, err + } + result := make([]string, len(dirEntries)) + for i := range dirEntries { + result[i] = dirEntries[i].Name() + } + return result, nil +} + // ReadFile delegates to ioutil.ReadFile. func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) } diff --git a/api/types/replacement.go b/api/types/replacement.go index 46618b68b..1f59239d9 100644 --- a/api/types/replacement.go +++ b/api/types/replacement.go @@ -28,10 +28,10 @@ type SourceSelector struct { resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"` // Structured field path expected in the allowed object. - FieldPath string `json:"fieldPath" yaml:"fieldPath"` + FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` // Used to refine the interpretation of the field. - Options *FieldOptions `json:"options" yaml:"options"` + Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` } func (s *SourceSelector) String() string { @@ -54,34 +54,34 @@ type TargetSelector struct { Select *Selector `json:"select" yaml:"select"` // From the allowed set, remove objects that match this. - Reject []*Selector `json:"reject" yaml:"reject"` + Reject []*Selector `json:"reject,omitempty" yaml:"reject,omitempty"` // Structured field paths expected in each allowed object. - FieldPaths []string `json:"fieldPaths" yaml:"fieldPaths"` + FieldPaths []string `json:"fieldPaths,omitempty" yaml:"fieldPaths,omitempty"` // Used to refine the interpretation of the field. - Options *FieldOptions `json:"options" yaml:"options"` + Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` } // FieldOptions refine the interpretation of FieldPaths. type FieldOptions struct { // Used to split/join the field. - Delimiter string `json:"delimiter" yaml:"delimiter"` + Delimiter string `json:"delimiter,omitempty" yaml:"delimiter,omitempty"` // Which position in the split to consider. - Index int `json:"index" yaml:"index"` + Index int `json:"index,omitempty" yaml:"index,omitempty"` // TODO (#3492): Implement use of this option // None, Base64, URL, Hex, etc - Encoding string `json:"encoding" yaml:"encoding"` + Encoding string `json:"encoding,omitempty" yaml:"encoding,omitempty"` // If field missing, add it. - Create bool `json:"create" yaml:"create"` + Create bool `json:"create,omitempty" yaml:"create,omitempty"` } func (fo *FieldOptions) String() string { - if fo == nil || fo.Delimiter == "" { + if fo == nil || (fo.Delimiter == "" && !fo.Create) { return "" } - return fmt.Sprintf("%s(%d)", fo.Delimiter, fo.Index) + return fmt.Sprintf("%s(%d), create=%t", fo.Delimiter, fo.Index, fo.Create) } diff --git a/api/types/selector.go b/api/types/selector.go index 4ad1eb130..2c07f0b01 100644 --- a/api/types/selector.go +++ b/api/types/selector.go @@ -28,6 +28,10 @@ type Selector struct { LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"` } +func (s *Selector) Copy() Selector { + return *s +} + func (s *Selector) String() string { return fmt.Sprintf( "%s:a=%s:l=%s", s.ResId, s.AnnotationSelector, s.LabelSelector)