<commit_before>package discovery

import (
	"crypto/sha256"
	"io"
	"os"

	"github.com/blang/semver"
)

// PluginMeta is metadata about a plugin, useful for launching the plugin
// and for understanding which plugins are available.
type PluginMeta struct {
	// Name is the name of the plugin, e.g. as inferred from the plugin
	// binary's filename, or by explicit configuration.
	Name string

	// Version is the semver version of the plugin, expressed as a string
	// that might not be semver-valid. (Call VersionObj to attempt to
	// parse it and thus detect if it is invalid.)
	Version string

	// Path is the absolute path of the executable that can be launched
	// to provide the RPC server for this plugin.
	Path string
}

// VersionObj returns the semver version of the plugin as an object, or
// an error if the version string is not semver-syntax-compliant.
func (m PluginMeta) VersionObj() (semver.Version, error) {
	return semver.Make(m.Version)
}

// SHA256 returns a SHA256 hash of the content of the referenced executable
// file, or an error if the file's contents cannot be read.
func (m PluginMeta) SHA256() ([]byte, error) {
	f, err := os.Open(m.Path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	h := sha256.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return nil, err
	}

	return h.Sum(nil), nil
}<commit_msg>plugin/discovery: build plugin clients<commit_after>package discovery

import (
	"crypto/sha256"
	"io"
	"os"
	"os/exec"

	"github.com/blang/semver"
	plugin "github.com/hashicorp/go-plugin"
	tfplugin "github.com/hashicorp/terraform/plugin"
)

// PluginMeta is metadata about a plugin, useful for launching the plugin
// and for understanding which plugins are available.
type PluginMeta struct {
	// Name is the name of the plugin, e.g. as inferred from the plugin
	// binary's filename, or by explicit configuration.
	Name string

	// Version is the semver version of the plugin, expressed as a string
	// that might not be semver-valid. (Call VersionObj to attempt to
	// parse it and thus detect if it is invalid.)
	Version string

	// Path is the absolute path of the executable that can be launched
	// to provide the RPC server for this plugin.
	Path string
}

// VersionObj returns the semver version of the plugin as an object, or
// an error if the version string is not semver-syntax-compliant.
func (m PluginMeta) VersionObj() (semver.Version, error) {
	return semver.Make(m.Version)
}

// SHA256 returns a SHA256 hash of the content of the referenced executable
// file, or an error if the file's contents cannot be read.
func (m PluginMeta) SHA256() ([]byte, error) {
	f, err := os.Open(m.Path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	h := sha256.New()
	_, err = io.Copy(h, f)
	if err != nil {
		return nil, err
	}

	return h.Sum(nil), nil
}

// ClientConfig returns a configuration object that can be used to instantiate
// a client for the referenced plugin.
func (m PluginMeta) ClientConfig() *plugin.ClientConfig {
	return &plugin.ClientConfig{
		Cmd:             exec.Command(m.Path),
		HandshakeConfig: tfplugin.Handshake,
		Managed:         true,
		Plugins:         tfplugin.PluginMap,
	}
}

// Client returns a plugin client for the referenced plugin.
func (m PluginMeta) Client() *plugin.Client {
	return plugin.NewClient(m.ClientConfig())
}
{'fraction_non_alphanumeric': 0.09602649006622517, 'fraction_numerical': 0.009030704394942806, 'mean_word_length': 3.454423592493298, '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': '1844767', 'n_tokens_mistral': 1075, 'n_tokens_neox': 1004, 'n_words': 432}