bitbucket.org/ai69/amoy@v0.2.3/line.go (about)

     1  package amoy
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  )
    10  
    11  // LineFunc stands for a handler for each line string.
    12  type LineFunc func(line string) (err error)
    13  
    14  //revive:disable:error-naming It's not a real error
    15  var (
    16  	// QuitRead indicates the arbitrary error means to quit from reading.
    17  	QuitRead = errors.New("amoy: quit read by line")
    18  )
    19  
    20  // ReadFileByLine iterates the given file by lines (the line ending chars are not included).
    21  func ReadFileByLine(path string, callback LineFunc) (err error) {
    22  	var file *os.File
    23  	if file, err = os.Open(path); err != nil {
    24  		return
    25  	}
    26  	defer file.Close()
    27  	return ReadByLine(file, callback)
    28  }
    29  
    30  // ReadByLine iterates the given Reader by lines (the line ending chars are not included).
    31  func ReadByLine(rd io.Reader, callback LineFunc) (err error) {
    32  	readLine := func(r *bufio.Reader) (string, error) {
    33  		var (
    34  			err      error
    35  			line, ln []byte
    36  			isPrefix = true
    37  		)
    38  		for isPrefix && err == nil {
    39  			line, isPrefix, err = r.ReadLine()
    40  			ln = append(ln, line...)
    41  		}
    42  		return string(ln), err
    43  	}
    44  	r := bufio.NewReader(rd)
    45  	s, e := readLine(r)
    46  	for e == nil {
    47  		if err = callback(s); err != nil {
    48  			break
    49  		}
    50  		s, e = readLine(r)
    51  	}
    52  
    53  	if err == QuitRead {
    54  		err = nil
    55  	}
    56  	return
    57  }
    58  
    59  // ReadFileLines reads all lines from the given file (the line ending chars are not included).
    60  func ReadFileLines(path string) (lines []string, err error) {
    61  	err = ReadFileByLine(path, func(l string) error {
    62  		lines = append(lines, l)
    63  		return nil
    64  	})
    65  	return
    66  }
    67  
    68  // ReadLines reads all lines from the given reader (the line ending chars are not included).
    69  func ReadLines(rd io.Reader) (lines []string, err error) {
    70  	err = ReadByLine(rd, func(l string) error {
    71  		lines = append(lines, l)
    72  		return nil
    73  	})
    74  	return
    75  }
    76  
    77  // CountFileLines counts all lines from the given file (the line ending chars are not included).
    78  func CountFileLines(path string) (count int, err error) {
    79  	err = ReadFileByLine(path, func(l string) error {
    80  		count++
    81  		return nil
    82  	})
    83  	return
    84  }
    85  
    86  // CountLines counts all lines from the given reader (the line ending chars are not included).
    87  func CountLines(rd io.Reader) (count int, err error) {
    88  	err = ReadByLine(rd, func(l string) error {
    89  		count++
    90  		return nil
    91  	})
    92  	return
    93  }
    94  
    95  // WriteLines writes the given lines to a Writer.
    96  func WriteLines(wr io.Writer, lines []string) error {
    97  	w := bufio.NewWriter(wr)
    98  	defer w.Flush()
    99  	for _, line := range lines {
   100  		if _, err := fmt.Fprintln(w, line); err != nil {
   101  			return err
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // WriteFileLines writes the given lines as a text file.
   108  func WriteFileLines(path string, lines []string) error {
   109  	return openFileWriteLines(path, createFileFlag, lines)
   110  }
   111  
   112  // AppendFileLines appends the given lines to the end of a text file.
   113  func AppendFileLines(path string, lines []string) error {
   114  	return openFileWriteLines(path, appendFileFlag, lines)
   115  }
   116  
   117  func openFileWriteLines(path string, flag int, lines []string) error {
   118  	file, err := os.OpenFile(path, flag, filePerm)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	defer file.Close()
   123  	return WriteLines(file, lines)
   124  }
   125  
   126  // ExtractTopLines extracts the top n lines from the given stream (the line ending chars are not included), or lesser lines if the given stream doesn't contain enough line ending chars.
   127  func ExtractTopLines(rd io.Reader, n int) ([]string, error) {
   128  	if n <= 0 {
   129  		return nil, errors.New("amoy: n should be greater than 0")
   130  	}
   131  	result := make([]string, 0)
   132  	if err := ReadByLine(rd, func(line string) error {
   133  		result = append(result, line)
   134  		n--
   135  		if n <= 0 {
   136  			return QuitRead
   137  		}
   138  		return nil
   139  	}); err != nil {
   140  		return nil, err
   141  	}
   142  	return result, nil
   143  }
   144  
   145  // ExtractFirstLine extracts the first line from the given stream (the line ending chars are not included).
   146  func ExtractFirstLine(rd io.Reader) (string, error) {
   147  	lines, err := ExtractTopLines(rd, 1)
   148  	if err != nil {
   149  		return EmptyStr, err
   150  	}
   151  	if len(lines) < 1 {
   152  		return EmptyStr, nil
   153  	}
   154  	return lines[0], nil
   155  }
   156  
   157  // ExtractBottomLines extracts the bottom n lines from the given stream (the line ending chars are not included), or lesser lines if the given stream doesn't contain enough line ending chars.
   158  func ExtractBottomLines(rd io.Reader, n int) ([]string, error) {
   159  	if n <= 0 {
   160  		return nil, errors.New("amoy: n should be greater than 0")
   161  	}
   162  	var (
   163  		result = make([]string, n, n)
   164  		cnt    int
   165  	)
   166  	if err := ReadByLine(rd, func(line string) error {
   167  		result[cnt%n] = line
   168  		cnt++
   169  		return nil
   170  	}); err != nil {
   171  		return nil, err
   172  	}
   173  	if cnt <= n {
   174  		return result[0:cnt], nil
   175  	}
   176  	pos := cnt % n
   177  	return append(result[pos:], result[0:pos]...), nil
   178  }
   179  
   180  // ExtractLastLine extracts the last line from the given stream (the line ending chars are not included).
   181  func ExtractLastLine(rd io.Reader) (string, error) {
   182  	lines, err := ExtractBottomLines(rd, 1)
   183  	if err != nil {
   184  		return EmptyStr, err
   185  	}
   186  	if len(lines) < 1 {
   187  		return EmptyStr, nil
   188  	}
   189  	return lines[len(lines)-1], nil
   190  }