1 package fs
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "path/filepath"
8
9 "gotest.tools/v3/assert"
10 )
11
12
13
14 type Manifest struct {
15 root *directory
16 }
17
18 type resource struct {
19 mode os.FileMode
20 uid uint32
21 gid uint32
22 }
23
24 type file struct {
25 resource
26 content io.ReadCloser
27 ignoreCariageReturn bool
28 compareContentFunc func(b []byte) CompareResult
29 }
30
31 func (f *file) Type() string {
32 return "file"
33 }
34
35 type symlink struct {
36 resource
37 target string
38 }
39
40 func (f *symlink) Type() string {
41 return "symlink"
42 }
43
44 type directory struct {
45 resource
46 items map[string]dirEntry
47 filepathGlobs map[string]*filePath
48 }
49
50 func (f *directory) Type() string {
51 return "directory"
52 }
53
54 type dirEntry interface {
55 Type() string
56 }
57
58
59
60
61 func ManifestFromDir(t assert.TestingT, path string) Manifest {
62 if ht, ok := t.(helperT); ok {
63 ht.Helper()
64 }
65
66 manifest, err := manifestFromDir(path)
67 assert.NilError(t, err)
68 return manifest
69 }
70
71 func manifestFromDir(path string) (Manifest, error) {
72 info, err := os.Stat(path)
73 switch {
74 case err != nil:
75 return Manifest{}, err
76 case !info.IsDir():
77 return Manifest{}, fmt.Errorf("path %s must be a directory", path)
78 }
79
80 directory, err := newDirectory(path, info)
81 return Manifest{root: directory}, err
82 }
83
84 func newDirectory(path string, info os.FileInfo) (*directory, error) {
85 items := make(map[string]dirEntry)
86 children, err := os.ReadDir(path)
87 if err != nil {
88 return nil, err
89 }
90 for _, child := range children {
91 fullPath := filepath.Join(path, child.Name())
92 items[child.Name()], err = getTypedResource(fullPath, child)
93 if err != nil {
94 return nil, err
95 }
96 }
97
98 return &directory{
99 resource: newResourceFromInfo(info),
100 items: items,
101 filepathGlobs: make(map[string]*filePath),
102 }, nil
103 }
104
105 func getTypedResource(path string, entry os.DirEntry) (dirEntry, error) {
106 info, err := entry.Info()
107 if err != nil {
108 return nil, err
109 }
110 switch {
111 case info.IsDir():
112 return newDirectory(path, info)
113 case info.Mode()&os.ModeSymlink != 0:
114 return newSymlink(path, info)
115
116 default:
117 return newFile(path, info)
118 }
119 }
120
121 func newSymlink(path string, info os.FileInfo) (*symlink, error) {
122 target, err := os.Readlink(path)
123 if err != nil {
124 return nil, err
125 }
126 return &symlink{
127 resource: newResourceFromInfo(info),
128 target: target,
129 }, err
130 }
131
132 func newFile(path string, info os.FileInfo) (*file, error) {
133
134 readCloser, err := os.Open(path)
135 if err != nil {
136 return nil, err
137 }
138 return &file{
139 resource: newResourceFromInfo(info),
140 content: readCloser,
141 }, err
142 }
143
View as plain text