github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/io.go (about)

     1  // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License.
     2  
     3  package runner
     4  
     5  // This file contains routines for performing file io
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"io"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  
    15  	"github.com/karlmutch/circbuf"
    16  	"github.com/karlmutch/vtclean"
    17  
    18  	"github.com/go-stack/stack"
    19  	"github.com/jjeffery/kv" // MIT License
    20  )
    21  
    22  // ReadLast will extract the last portion of data from a file up to a maximum specified by
    23  // the caller.
    24  //
    25  func ReadLast(fn string, max uint32) (data string, err kv.Error) {
    26  	file, errOs := os.Open(filepath.Clean(fn))
    27  	if errOs != nil {
    28  		return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime())
    29  	}
    30  	defer file.Close()
    31  
    32  	fi, errOs := file.Stat()
    33  	if errOs != nil {
    34  		return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime())
    35  	}
    36  
    37  	// Suck up a lot of data to allow us to process lines with backspaces etc and still be left with
    38  	// something useful
    39  	//
    40  	buf := make([]byte, 1024*1024)
    41  	readStart := fi.Size() - int64(len(buf))
    42  
    43  	if readStart <= 0 {
    44  		readStart = 0
    45  	}
    46  
    47  	n, errOs := file.ReadAt(buf, readStart)
    48  	if errOs != nil && errOs != io.EOF {
    49  		return "", kv.Wrap(errOs, fn).With("stack", stack.Trace().TrimRuntime())
    50  	}
    51  
    52  	ring, _ := circbuf.NewBuffer(int64(max))
    53  	s := bufio.NewScanner(bytes.NewReader(buf[:n]))
    54  	for s.Scan() {
    55  		ring.Write([]byte(vtclean.Clean(s.Text(), true)))
    56  		ring.Write([]byte{'\n'})
    57  	}
    58  	return string(ring.Bytes()), nil
    59  }
    60  
    61  // DetectFileType can be used to examine the contexts of a file and return
    62  // the most likely match for its contents as a mime type.
    63  //
    64  func DetectFileType(fn string) (typ string, err kv.Error) {
    65  	file, errOs := os.Open(filepath.Clean(fn))
    66  	if errOs != nil {
    67  		return "", kv.Wrap(errOs).With("filename", fn).With("stack", stack.Trace().TrimRuntime())
    68  	}
    69  	defer file.Close()
    70  
    71  	// Only the first 512 bytes are used to sniff the content type.
    72  	buffer := make([]byte, 512)
    73  	if _, errOs = file.Read(buffer); errOs != nil && errOs != io.EOF {
    74  		return "", kv.Wrap(errOs).With("filename", fn).With("stack", stack.Trace().TrimRuntime())
    75  	}
    76  
    77  	// Always returns a valid content-type and "application/octet-stream" if no others seemed to match.
    78  	return http.DetectContentType(buffer), nil
    79  }
    80  
    81  // CopyFile is a simple file copy that will overwrite any destination
    82  //
    83  func CopyFile(srcFN string, dstFN string) (n int64, err kv.Error) {
    84  	stat, errGo := os.Stat(srcFN)
    85  	if errGo != nil {
    86  		return 0, kv.Wrap(errGo).With("source", src).With("stack", stack.Trace().TrimRuntime())
    87  	}
    88  
    89  	if !stat.Mode().IsRegular() {
    90  		return 0, kv.NewError("not a regular file").With("source", src).With("stack", stack.Trace().TrimRuntime())
    91  	}
    92  
    93  	src, errGo := os.Open(srcFN)
    94  	if errGo != nil {
    95  		return 0, kv.Wrap(errGo).With("source", srcFN).With("stack", stack.Trace().TrimRuntime())
    96  	}
    97  	defer src.Close()
    98  
    99  	dst, errGo := os.Create(dstFN)
   100  	if err != nil {
   101  		return 0, kv.Wrap(errGo).With("dst", dstFN).With("stack", stack.Trace().TrimRuntime())
   102  	}
   103  	defer dst.Close()
   104  
   105  	if n, errGo = io.Copy(dst, src); errGo != nil {
   106  		return 0, kv.Wrap(errGo).With("source", src, "dst", dstFN).With("stack", stack.Trace().TrimRuntime())
   107  	}
   108  	return n, nil
   109  }