<commit_before>package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/internal/depgraph"
	"github.com/zombiezen/mcm/third_party/golang/capnproto"
)

func main() {
	c, err := readCatalog(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash: read catalog:", err)
		os.Exit(1)
	}
	res, _ := c.Resources()
	g, err := depgraph.New(res)
	if err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash:", err)
		os.Exit(1)
	}
	if err = writeScript(os.Stdout, g); err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash:", err)
		os.Exit(1)
	}
}

func writeScript(w io.Writer, g *depgraph.Graph) error {
	ew := &errWriter{w: w}
	io.WriteString(ew, "#!/bin/bash\n_() {\nset -e\n")
	for ew.err == nil && !g.Done() {
		ready := append([]uint64(nil), g.Ready()...)
		if len(ready) == 0 {
			return errors.New("graph not done, but has nothing to do")
		}
		for _, id := range ready {
			if err := scriptResource(ew, g.Resource(id)); err != nil {
				return fmt.Errorf("resource ID=%d: %v", id, err)
			}
			g.Mark(id)
		}
	}
	io.WriteString(ew, "}\n_ \"$0\" \"$@\"\n")
	return ew.err
}

func scriptResource(ew *errWriter, r catalog.Resource) error {
	f, _ := r.File()
	path, err := f.Path()
	if err != nil {
		return fmt.Errorf("reading file path: %v", err)
	} else if path == "" {
		return errors.New("file path is empty")
	}
	switch f.Which() {
	case catalog.File_Which_plain:
		// TODO(soon): touch, even if no content
		// TODO(soon): respect file mode
		if f.Plain().HasContent() {
			fmt.Fprintf(ew, "base64 -d > %s <<!EOF!\n", shellQuote(path))
			content, _ := f.Plain().Content()
			enc := base64.NewEncoder(base64.StdEncoding, ew)
			enc.Write(content)
			enc.Close()
			io.WriteString(ew, "\n!EOF!\n")
		}
	case catalog.File_Which_directory:
		// TODO(soon): respect file mode
		fmt.Fprintf(ew, "if [[ ! -d %s ]]; then\n\tmkdir %[1]s\nfi\n", shellQuote(path))
	default:
		return fmt.Errorf("unsupported file directive %v", f.Which())
	}
	return nil
}

func readCatalog(r io.Reader) (catalog.Catalog, error) {
	msg, err := capnp.NewDecoder(r).Decode()
	if err != nil {<commit_msg>catbash: use comments in output<commit_after>package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"os"

	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/internal/depgraph"
	"github.com/zombiezen/mcm/third_party/golang/capnproto"
)

func main() {
	c, err := readCatalog(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash: read catalog:", err)
		os.Exit(1)
	}
	res, _ := c.Resources()
	g, err := depgraph.New(res)
	if err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash:", err)
		os.Exit(1)
	}
	if err = writeScript(os.Stdout, g); err != nil {
		fmt.Fprintln(os.Stderr, "mcm-catbash:", err)
		os.Exit(1)
	}
}

func writeScript(w io.Writer, g *depgraph.Graph) error {
	ew := &errWriter{w: w}
	io.WriteString(ew, "#!/bin/bash\n_() {\nset -e\n")
	for ew.err == nil && !g.Done() {
		ready := append([]uint64(nil), g.Ready()...)
		if len(ready) == 0 {
			return errors.New("graph not done, but has nothing to do")
		}
		for _, id := range ready {
			if err := scriptResource(ew, g.Resource(id)); err != nil {
				return fmt.Errorf("resource ID=%d: %v", id, err)
			}
			g.Mark(id)
		}
	}
	io.WriteString(ew, "}\n_ \"$0\" \"$@\"\n")
	return ew.err
}

func scriptResource(ew *errWriter, r catalog.Resource) error {
	if c, _ := r.Comment(); c != "" {
		fmt.Fprintf(ew, "\n# %s\n", c)
	} else {
		fmt.Fprintf(ew, "\n# Resource ID=%d\n", r.ID())
	}
	f, _ := r.File()
	path, err := f.Path()
	if err != nil {
		return fmt.Errorf("reading file path: %v", err)
	} else if path == "" {
		return errors.New("file path is empty")
	}
	switch f.Which() {
	case catalog.File_Which_plain:
		// TODO(soon): touch, even if no content
		// TODO(soon): respect file mode
		if f.Plain().HasContent() {
			fmt.Fprintf(ew, "base64 -d > %s <<!EOF!\n", shellQuote(path))
			content, _ := f.Plain().Content()
			enc := base64.NewEncoder(base64.StdEncoding, ew)
			enc.Write(content)
			enc.Close()
			io.WriteString(ew, "\n!EOF!\n")
		}
	case catalog.File_Which_directory:
		// TODO(soon): respect file mode
		fmt.Fprintf(ew, "if [[ ! -d %s ]]; then\n\tmkdir %[1]s\nfi\n", shellQuote(path))
	default:
		return fmt.Errorf("unsupported file directive %v", f.Which())
	}
	return nil
}

func readCatalog(r io.Reader) (catalog.Catalog, error) {
	msg, err := capnp.NewDecoder(r).Decode()
	if err != nil {
{'fraction_non_alphanumeric': 0.19664053751399777, 'fraction_numerical': 0.007166853303471445, 'mean_word_length': 3.502016129032258, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7442079', 'n_tokens_mistral': 1941, 'n_tokens_neox': 1815, 'n_words': 406}