<commit_before>package pwr

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	osync "sync"

	"github.com/dustin/go-humanize"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/sync"
	"github.com/itchio/wharf/tlc"
	"github.com/itchio/wharf/wire"
)

var (
	// ErrMalformedPatch is returned when a patch could not be parsed
	ErrMalformedPatch = errors.New("malformed patch")

	// ErrIncompatiblePatch is returned when a patch but parsing
	// and applying it is unsupported (e.g. it's a newer version of the format)
	ErrIncompatiblePatch = errors.New("unsupported patch")
)

type DataFetchFunction func([]byte, string) ([]byte, error)
type DataStoreFunction func([]byte) error

// ApplyContext holds the state while applying a patch
type ApplyContext struct {
	Consumer *StateConsumer

	TargetPath string
	OutputPath string
	InPlace    bool

	TargetContainer *tlc.Container
	TargetPool      sync.FilePool
	SourceContainer *tlc.Container

	SignatureFilePath string

	TouchedFiles int
	NoopFiles    int
	DeletedFiles int
	StageSize    int64

	DataFetch DataFetchFunction
	DataStore DataStoreFunction
}

type signature []sync.BlockHash
type signatureSet map[string]signature
type signatureResult struct {
	path string
	sig  signature
	err  error
}

// ApplyPatch reads a patch, parses it, and generates the new file tree
func (actx *ApplyContext) ApplyPatch(patchReader io.Reader) error {
	actualOutputPath := actx.OutputPath
	if actx.InPlace {
		// applying in-place is a bit tricky: we can't overwrite files in the
		// target directory (old) while we're reading the patch otherwise
		// we might be copying new bytes instead of old bytes into later files
		// so, we rebuild 'touched' files in a staging area
		stagePath := actualOutputPath + "-stage"
		defer os.RemoveAll(stagePath)
		actx.OutputPath = stagePath
	}

	rawPatchWire := wire.NewReadContext(patchReader)<commit_msg>Make stage path whatever happens (allow patches that just delete files)<commit_after>package pwr

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	osync "sync"

	"github.com/dustin/go-humanize"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/sync"
	"github.com/itchio/wharf/tlc"
	"github.com/itchio/wharf/wire"
)

var (
	// ErrMalformedPatch is returned when a patch could not be parsed
	ErrMalformedPatch = errors.New("malformed patch")

	// ErrIncompatiblePatch is returned when a patch but parsing
	// and applying it is unsupported (e.g. it's a newer version of the format)
	ErrIncompatiblePatch = errors.New("unsupported patch")
)

type DataFetchFunction func([]byte, string) ([]byte, error)
type DataStoreFunction func([]byte) error

// ApplyContext holds the state while applying a patch
type ApplyContext struct {
	Consumer *StateConsumer

	TargetPath string
	OutputPath string
	InPlace    bool

	TargetContainer *tlc.Container
	TargetPool      sync.FilePool
	SourceContainer *tlc.Container

	SignatureFilePath string

	TouchedFiles int
	NoopFiles    int
	DeletedFiles int
	StageSize    int64

	DataFetch DataFetchFunction
	DataStore DataStoreFunction
}

type signature []sync.BlockHash
type signatureSet map[string]signature
type signatureResult struct {
	path string
	sig  signature
	err  error
}

// ApplyPatch reads a patch, parses it, and generates the new file tree
func (actx *ApplyContext) ApplyPatch(patchReader io.Reader) error {
	actualOutputPath := actx.OutputPath
	if actx.InPlace {
		// applying in-place is a bit tricky: we can't overwrite files in the
		// target directory (old) while we're reading the patch otherwise
		// we might be copying new bytes instead of old bytes into later files
		// so, we rebuild 'touched' files in a staging area
		stagePath := actualOutputPath + "-stage"
		err := os.MkdirAll(stagePath, os.FileMode(0755))
		if err != nil {
			return err
		}

		defer os.RemoveAll(stagePath)
		actx.OutputPath = stagePath
	}

	rawPatchWire := wire.NewReadContext(patchReader)
{'fraction_non_alphanumeric': 0.0844188376753507, 'fraction_numerical': 0.002004008016032064, 'mean_word_length': 4.288741721854304, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '2700758', 'n_tokens_mistral': 1305, 'n_tokens_neox': 1228, 'n_words': 405}