<commit_before>// Copyright 2016 The Minimal Configuration Manager Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package shlib provides the functionality of the mcm-shellify tool.
package shlib

import (
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	slashpath "path"
	"strconv"

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

// WriteScript converts a catalog into a bash script and writes it to w.
func WriteScript(w io.Writer, c catalog.Catalog) error {
	g := newGen(w)
	g.p(script("#!/bin/bash"))
	g.p(script("# Autogenerated by mcm-shellify"))
	g.p()
	res, _ := c.Resources()
	if res.Len() == 0 {
		g.p(script("# Empty catalog"))
		return g.ew.err
	}
	graph, err := depgraph.New(res)
	if err != nil {
		return err
	}
	for i := 0; i < res.Len(); i++ {
		r := res.At(i)
		if err := g.resourceFunc(r); err != nil {
			return fmt.Errorf("resource ID=%d: %v", r.ID(), err)
		}
	}
	g.p(script("_() {"))
	g.in()
	for i := 0; i < res.Len(); i++ {
		v := resourceStatusVar(res.At(i).ID())
		g.p(assignment{v, -2})
	}
	for g.ew.err == nil && !graph.Done() {
		ready := append([]uint64(nil), graph.Ready()...)
		if len(ready) == 0 {
			return errors.New("graph not done, but has nothing to do")
		}
		for _, id := range ready {
			graph.Mark(id)
			deps, _ := graph.Resource(id).Dependencies()
			if deps.Len() == 0 {
				g.p(resourceFuncName(id))
				continue
			}
			g.p(script("if [["), depsPrecondition(deps), script("]]; then"))
			g.in()
			g.p(resourceFuncName(id))
			g.out()
			g.p(script("else"))
			g.in()
			g.p(assignment{resourceStatusVar(id), -1})
			g.out()
			g.p(script("fi"))
		}
	}
	g.exitStatusCheck(res)
	g.out()
	g.p(script("}"))
	g.p(script(`_ "$0" "$@"`))
	return g.ew.err
}

func resourceStatusVar(id uint64) script {
	return script(fmt.Sprintf("status%d", id))
}

func resourceFuncName(id uint64) script {
	return script(fmt.Sprintf("resource%d", id))
}

func (g *gen) resourceFunc(r catalog.Resource) error {
	id := r.ID()
	if c, _ := r.Comment(); c != "" {
		// TODO(someday): trim newlines?
		g.p(script("#"), script(c))
	}
	g.p(resourceFuncName(id) + "() {")
	defer g.p(script("}"))
	g.in()
	defer g.out()

	statVar := resourceStatusVar(id)
	switch r.Which() {
	case catalog.Resource_Which_noop:
		if deps, _ := r.Dependencies(); deps.Len() > 0 {
			g.p(script("[["), depsChangedCondition(deps), script("]] &&"), assignment{statVar, 1}, script("||"), assignment{statVar, 0})
		} else {
			g.p(assignment{statVar, 0})
		}
		return nil<commit_msg>shellify: add applying logs to script output<commit_after>// Copyright 2016 The Minimal Configuration Manager Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package shlib provides the functionality of the mcm-shellify tool.
package shlib

import (
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	slashpath "path"
	"strconv"

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

// WriteScript converts a catalog into a bash script and writes it to w.
func WriteScript(w io.Writer, c catalog.Catalog) error {
	g := newGen(w)
	g.p(script("#!/bin/bash"))
	g.p(script("# Autogenerated by mcm-shellify"))
	g.p()
	res, _ := c.Resources()
	if res.Len() == 0 {
		g.p(script("# Empty catalog"))
		return g.ew.err
	}
	graph, err := depgraph.New(res)
	if err != nil {
		return err
	}
	for i := 0; i < res.Len(); i++ {
		r := res.At(i)
		if err := g.resourceFunc(r); err != nil {
			return fmt.Errorf("resource ID=%d: %v", r.ID(), err)
		}
	}
	g.p(script("_() {"))
	g.in()
	for i := 0; i < res.Len(); i++ {
		v := resourceStatusVar(res.At(i).ID())
		g.p(assignment{v, -2})
	}
	for g.ew.err == nil && !graph.Done() {
		ready := append([]uint64(nil), graph.Ready()...)
		if len(ready) == 0 {
			return errors.New("graph not done, but has nothing to do")
		}
		for _, id := range ready {
			graph.Mark(id)
			deps, _ := graph.Resource(id).Dependencies()
			if deps.Len() == 0 {
				g.p(resourceFuncName(id))
				continue
			}
			g.p(script("if [["), depsPrecondition(deps), script("]]; then"))
			g.in()
			g.p(resourceFuncName(id))
			g.out()
			g.p(script("else"))
			g.in()
			g.p(assignment{resourceStatusVar(id), -1})
			g.out()
			g.p(script("fi"))
		}
	}
	g.exitStatusCheck(res)
	g.out()
	g.p(script("}"))
	g.p(script(`_ "$0" "$@"`))
	return g.ew.err
}

func resourceStatusVar(id uint64) script {
	return script(fmt.Sprintf("status%d", id))
}

func resourceFuncName(id uint64) script {
	return script(fmt.Sprintf("resource%d", id))
}

func (g *gen) resourceFunc(r catalog.Resource) error {
	id := r.ID()
	if c, _ := r.Comment(); c != "" {
		// TODO(someday): trim newlines?
		g.p(script("#"), script(c))
	}
	g.p(resourceFuncName(id) + "() {")
	defer g.p(script("}"))
	g.in()
	defer g.out()

	if c, _ := r.Comment(); c != "" {
		g.p(script("echo"), fmt.Sprintf("applying: %s (id=%d)", c, r.ID()), script("1>&2"))
	} else {
		g.p(script("echo"), fmt.Sprintf("applying: id=%d", r.ID()), script("1>&2"))
	}
	statVar := resourceStatusVar(id)
	switch r.Which() {
	case catalog.Resource_Which_noop:
		if deps, _ := r.Dependencies(); deps.Len() > 0 {
			g.p(script("[["), depsChangedCondition(deps), script("]] &&"), assignment{statVar, 1}, script("||"), assignment{statVar, 0})
		} else {
			g.p(assignment{statVar, 0})
		}
		return nil
{'fraction_non_alphanumeric': 0.1874903474903475, 'fraction_numerical': 0.009575289575289575, 'mean_word_length': 3.632331902718169, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '82624', 'n_tokens_mistral': 2571, 'n_tokens_neox': 2388, 'n_words': 618}