github.com/puellanivis/breton@v0.2.16/lib/files/about/about.go (about) 1 // Package aboutfiles implements a simple "about:" scheme. 2 package aboutfiles 3 4 import ( 5 "context" 6 "net/url" 7 "os" 8 "sort" 9 "strings" 10 "time" 11 12 "github.com/puellanivis/breton/lib/files" 13 "github.com/puellanivis/breton/lib/files/wrapper" 14 ) 15 16 type reader interface { 17 ReadAll() ([]byte, error) 18 } 19 20 type lister interface { 21 ReadDir() ([]os.FileInfo, error) 22 } 23 24 type aboutMap map[string]reader 25 26 func (m aboutMap) keys() []string { 27 var keys []string 28 29 for key := range m { 30 if key == "" || strings.HasPrefix(key, ".") { 31 continue 32 } 33 34 keys = append(keys, key) 35 } 36 37 sort.Strings(keys) 38 39 return keys 40 } 41 42 func (m aboutMap) ReadAll() ([]byte, error) { 43 var lines []string 44 45 for _, key := range m.keys() { 46 uri := &url.URL{ 47 Scheme: "about", 48 Opaque: url.PathEscape(key), 49 } 50 51 lines = append(lines, uri.String()) 52 } 53 54 return []byte(strings.Join(append(lines, ""), "\n")), nil 55 } 56 57 func (m aboutMap) ReadDir() ([]os.FileInfo, error) { 58 var infos []os.FileInfo 59 60 for _, key := range m.keys() { 61 uri := &url.URL{ 62 Scheme: "about", 63 Opaque: url.PathEscape(key), 64 } 65 66 infos = append(infos, wrapper.NewInfo(uri, 0, time.Now())) 67 } 68 69 return infos, nil 70 } 71 72 var ( 73 about = aboutMap{ 74 "": version, 75 "blank": blank, 76 "cache": blank, 77 "invalid": errorURL{os.ErrNotExist}, 78 "html-kind": errorURL{ErrNoSuchHost}, 79 "legacy-compat": errorURL{ErrNoSuchHost}, 80 "now": now, 81 "plugins": schemeList{}, 82 "srcdoc": errorURL{ErrNoSuchHost}, 83 "version": version, 84 } 85 ) 86 87 func init() { 88 // During initialization about is not allowed to reference about, 89 // else Go errors with "initialization loop". 90 about["about"] = about 91 } 92 93 func init() { 94 files.RegisterScheme(handler{}, "about") 95 } 96 97 type handler struct{} 98 99 func (h handler) Create(ctx context.Context, uri *url.URL) (files.Writer, error) { 100 return nil, &os.PathError{ 101 Op: "create", 102 Path: uri.String(), 103 Err: files.ErrNotSupported, 104 } 105 } 106 107 func (h handler) Open(ctx context.Context, uri *url.URL) (files.Reader, error) { 108 if uri.Host != "" || uri.User != nil { 109 return nil, &os.PathError{ 110 Op: "open", 111 Path: uri.String(), 112 Err: files.ErrURLCannotHaveAuthority, 113 } 114 } 115 116 path := uri.Path 117 if path == "" { 118 var err error 119 path, err = url.PathUnescape(uri.Opaque) 120 if err != nil { 121 return nil, &os.PathError{ 122 Op: "open", 123 Path: uri.String(), 124 Err: files.ErrURLInvalid, 125 } 126 } 127 } 128 129 f, ok := about[path] 130 if !ok { 131 return nil, &os.PathError{ 132 Op: "open", 133 Path: uri.String(), 134 Err: os.ErrNotExist, 135 } 136 } 137 138 data, err := f.ReadAll() 139 if err != nil { 140 return nil, &os.PathError{ 141 Op: "open", 142 Path: uri.String(), 143 Err: err, 144 } 145 } 146 147 return wrapper.NewReaderFromBytes(data, uri, time.Now()), nil 148 } 149 150 func (h handler) List(ctx context.Context, uri *url.URL) ([]os.FileInfo, error) { 151 return h.ReadDir(ctx, uri) 152 } 153 154 func (h handler) ReadDir(ctx context.Context, uri *url.URL) ([]os.FileInfo, error) { 155 if uri.Host != "" || uri.User != nil { 156 return nil, &os.PathError{ 157 Op: "readdir", 158 Path: uri.String(), 159 Err: files.ErrURLCannotHaveAuthority, 160 } 161 } 162 163 path := uri.Path 164 if path == "" { 165 var err error 166 path, err = url.PathUnescape(uri.Opaque) 167 if err != nil { 168 return nil, &os.PathError{ 169 Op: "readdir", 170 Path: uri.String(), 171 Err: files.ErrURLInvalid, 172 } 173 } 174 } 175 176 if path == "" { 177 path = "about" 178 } 179 180 f, ok := about[path] 181 if !ok { 182 return nil, &os.PathError{ 183 Op: "readdir", 184 Path: uri.String(), 185 Err: os.ErrNotExist, 186 } 187 } 188 189 l, ok := f.(lister) 190 if !ok { 191 return nil, &os.PathError{ 192 Op: "readdir", 193 Path: uri.String(), 194 Err: files.ErrNotDirectory, 195 } 196 } 197 198 infos, err := l.ReadDir() 199 if err != nil { 200 return nil, &os.PathError{ 201 Op: "readdir", 202 Path: uri.String(), 203 Err: err, 204 } 205 } 206 207 return infos, nil 208 }