github.com/brandur/modulir@v0.0.0-20240305213423-94ee82929cbd/modules/myaml/myaml.go (about)

     1  package myaml
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"os"
     7  
     8  	"golang.org/x/xerrors"
     9  	"gopkg.in/yaml.v2"
    10  
    11  	"github.com/brandur/modulir"
    12  )
    13  
    14  // ParseFile is a shortcut from parsing a source file as YAML.
    15  func ParseFile(c *modulir.Context, source string, v interface{}) error {
    16  	raw, err := os.ReadFile(source)
    17  	if err != nil {
    18  		return xerrors.Errorf("error reading file: %w", err)
    19  	}
    20  
    21  	err = yaml.Unmarshal(raw, v)
    22  	if err != nil {
    23  		return xerrors.Errorf("error unmarshaling YAML: %w", err)
    24  	}
    25  
    26  	c.Log.Debugf("myaml: Parsed file: %s", source)
    27  	return nil
    28  }
    29  
    30  // ParseFileFrontmatter is a shortcut from parsing a source file's frontmatter
    31  // (i.e. data at the top between `---` lines) as YAML.
    32  func ParseFileFrontmatter(c *modulir.Context, source string, v interface{}) ([]byte, error) {
    33  	data, err := os.ReadFile(source)
    34  	if err != nil {
    35  		return nil, xerrors.Errorf("error reading file: %w", err)
    36  	}
    37  
    38  	frontmatter, content, err := splitFrontmatter(data)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	err = yaml.Unmarshal(frontmatter, v)
    44  	if err != nil {
    45  		return nil, xerrors.Errorf("error unmarshaling YAML frontmatter: %w", err)
    46  	}
    47  
    48  	c.Log.Debugf("myaml: Parsed file frontmatter: %s", source)
    49  	return content, nil
    50  }
    51  
    52  //
    53  // Private
    54  //
    55  
    56  var errBadFrontmatter = errors.New("error splitting YAML frontmatter")
    57  
    58  func splitFrontmatter(data []byte) ([]byte, []byte, error) {
    59  	parts := bytes.Split(data, []byte("---\n"))
    60  
    61  	switch {
    62  	case len(parts) > 1 && !bytes.Equal(parts[0], []byte("")):
    63  		return nil, nil, errBadFrontmatter
    64  	case len(parts) == 2:
    65  		return nil, bytes.TrimSpace(parts[1]), nil
    66  	case len(parts) == 3:
    67  		return bytes.TrimSpace(parts[1]), bytes.TrimSpace(parts[2]), nil
    68  	}
    69  
    70  	return nil, bytes.TrimSpace(parts[0]), nil
    71  }