github.com/rgonomic/rgo@v0.2.2-0.20220708095818-4747f0905d6e/internal/vfs/txtar/txtar.go (about)

     1  // Copyright ©2019 The rgonomic Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package txtar provides a simple write-only filesystem based on the txtar archive format.
     6  package txtar
     7  
     8  import (
     9  	"bytes"
    10  	"io"
    11  	"os"
    12  	"sort"
    13  
    14  	"golang.org/x/tools/txtar"
    15  )
    16  
    17  // FileSystem is a write-only filesystem based on the txtar text archive format
    18  // provided by golang.org/x/tools/txtar.
    19  type FileSystem struct {
    20  	// Output is the destination of the filesystem
    21  	// when the Flush method is called. If Output
    22  	// is nil, data is written to os.Stdout.
    23  	Output io.Writer
    24  	ar     txtar.Archive
    25  	buf    bytes.Buffer
    26  }
    27  
    28  // Open returns a new io.WriteCloser file whose writes will added to the
    29  // txtar output of the Filesystem with the given path. Writes to the
    30  // file are buffered until the Flush method is called. The file must
    31  // be closed before calling Flush.
    32  func (fs *FileSystem) Open(path string) (io.WriteCloser, error) {
    33  	return file{path: path, buf: &fs.buf, owner: fs}, nil
    34  }
    35  
    36  func (fs *FileSystem) add(path string, data []byte) {
    37  	fs.ar.Files = append(fs.ar.Files, txtar.File{
    38  		Name: path,
    39  		Data: data,
    40  	})
    41  }
    42  
    43  // Flush writes all closed io.Writers to the FileSystem's Output
    44  // or os.Stdout if Output is nil. The files in the txtar are sorted
    45  // lexically by name.
    46  func (fs *FileSystem) Flush() error {
    47  	sort.Sort(byPath(fs.ar.Files))
    48  	var dst io.Writer = os.Stdout
    49  	if fs.Output != nil {
    50  		dst = fs.Output
    51  	}
    52  	_, err := dst.Write(txtar.Format(&fs.ar))
    53  	return err
    54  }
    55  
    56  type file struct {
    57  	path  string
    58  	buf   *bytes.Buffer
    59  	owner *FileSystem
    60  }
    61  
    62  func (f file) Write(b []byte) (int, error) {
    63  	return f.buf.Write(b)
    64  }
    65  
    66  func (f file) Close() error {
    67  	b := make([]byte, f.buf.Len())
    68  	copy(b, f.buf.Bytes())
    69  	f.owner.add(f.path, b)
    70  	f.buf.Reset()
    71  	return nil
    72  }
    73  
    74  type byPath []txtar.File
    75  
    76  func (f byPath) Len() int           { return len(f) }
    77  func (f byPath) Less(i, j int) bool { return f[i].Name < f[j].Name }
    78  func (f byPath) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }