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  }