<commit_before>package gocb

import (
	"context"
	"encoding/json"
	"sync"
	"time"

	"github.com/couchbase/gocbcore/v8"
	"github.com/google/uuid"
)

// PingServiceEntry represents a single entry in a ping report.
type PingServiceEntry struct {
	Service  ServiceType
	Endpoint string
	Success  bool
	Latency  time.Duration
}

// PingReport encapsulates the details from a executed ping operation.
type PingReport struct {
	Services []PingServiceEntry
	ID       string
}

type jsonPingServiceEntry struct {
	Remote    string `json:"remote"`
	LatencyUs uint64 `json:"latency_us"`
	Success   bool   `json:"success"`
}

type jsonPingReport struct {
	Version  int                               `json:"version"`
	ID       string                            `json:"id"`
	Sdk      string                            `json:"sdk"`
	Services map[string][]jsonPingServiceEntry `json:"services"`
}

// MarshalJSON generates a JSON representation of this ping report.
func (report *PingReport) MarshalJSON() ([]byte, error) {
	jsonReport := jsonPingReport{
		Version:  1,
		ID:       report.ID,
		Sdk:      Identifier() + " " + "gocbcore/" + gocbcore.Version(),
		Services: make(map[string][]jsonPingServiceEntry),
	}

	for _, service := range report.Services {
		serviceStr := diagServiceString(service.Service)
		jsonReport.Services[serviceStr] = append(jsonReport.Services[serviceStr], jsonPingServiceEntry{
			Remote:    service.Endpoint,
			LatencyUs: uint64(service.Latency / time.Nanosecond),
			Success:   service.Success,
		})
	}

	return json.Marshal(&jsonReport)
}

func (jsonReport *jsonPingReport) toReport() *PingReport {
	report := &PingReport{
		ID: jsonReport.ID,
	}

	for key, jsonServices := range jsonReport.Services {
		for _, jsonService := range jsonServices {
			report.Services = append(report.Services, PingServiceEntry{
				Service:  diagStringService(key),
				Endpoint: jsonService.Remote,
				Latency:  time.Duration(jsonService.LatencyUs) * time.Nanosecond,
				Success:  jsonService.Success,
			})
		}
	}

	return report
}

func (b *Bucket) pingKv(provider kvProvider) (pingsOut []gocbcore.PingResult, errOut error) {
	signal := make(chan bool, 1)

	op, err := provider.PingKvEx(gocbcore.PingKvOptions{}, func(services *gocbcore.PingKvResult, err error) {
		if err != nil {
			errOut = err
			signal <- true
			return
		}

		results := services.Services
		pingsOut = make([]gocbcore.PingResult, len(results))
		for pingIdx, ping := range results {
			pingsOut[pingIdx] = ping
		}
		signal <- true
	})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.sb.KvTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, timeoutError{}
	}
}

// PingOptions are the options available to the Ping operation.
type PingOptions struct {
	ServiceTypes []ServiceType
	ReportID     string
}

// Ping will ping a list of services and verify they are active and
// responding in an acceptable period of time.
//
// Volatile: This API is subject to change at any time.
func (b *Bucket) Ping(opts *PingOptions) (*PingReport, error) {
	if opts == nil {
		opts = &PingOptions{}
	}

	numServices := 0
	waitCh := make(chan error, 10)
	report := &PingReport{}
	var reportLock sync.Mutex
	services := opts.ServiceTypes

	report.ID = opts.ReportID
	if report.ID == "" {
		report.ID = uuid.New().String()<commit_msg>GOCBC-548: Rename PingReport to PingResult<commit_after>package gocb

import (
	"context"
	"encoding/json"
	"sync"
	"time"

	"github.com/couchbase/gocbcore/v8"
	"github.com/google/uuid"
)

// PingServiceEntry represents a single entry in a ping report.
type PingServiceEntry struct {
	Service  ServiceType
	Endpoint string
	Success  bool
	Latency  time.Duration
}

// PingResult encapsulates the details from a executed ping operation.
type PingResult struct {
	Services []PingServiceEntry
	ID       string
}

type jsonPingServiceEntry struct {
	Remote    string `json:"remote"`
	LatencyUs uint64 `json:"latency_us"`
	Success   bool   `json:"success"`
}

type jsonPingReport struct {
	Version  int                               `json:"version"`
	ID       string                            `json:"id"`
	Sdk      string                            `json:"sdk"`
	Services map[string][]jsonPingServiceEntry `json:"services"`
}

// MarshalJSON generates a JSON representation of this ping report.
func (report *PingResult) MarshalJSON() ([]byte, error) {
	jsonReport := jsonPingReport{
		Version:  1,
		ID:       report.ID,
		Sdk:      Identifier() + " " + "gocbcore/" + gocbcore.Version(),
		Services: make(map[string][]jsonPingServiceEntry),
	}

	for _, service := range report.Services {
		serviceStr := diagServiceString(service.Service)
		jsonReport.Services[serviceStr] = append(jsonReport.Services[serviceStr], jsonPingServiceEntry{
			Remote:    service.Endpoint,
			LatencyUs: uint64(service.Latency / time.Nanosecond),
			Success:   service.Success,
		})
	}

	return json.Marshal(&jsonReport)
}

func (jsonReport *jsonPingReport) toReport() *PingResult {
	report := &PingResult{
		ID: jsonReport.ID,
	}

	for key, jsonServices := range jsonReport.Services {
		for _, jsonService := range jsonServices {
			report.Services = append(report.Services, PingServiceEntry{
				Service:  diagStringService(key),
				Endpoint: jsonService.Remote,
				Latency:  time.Duration(jsonService.LatencyUs) * time.Nanosecond,
				Success:  jsonService.Success,
			})
		}
	}

	return report
}

func (b *Bucket) pingKv(provider kvProvider) (pingsOut []gocbcore.PingResult, errOut error) {
	signal := make(chan bool, 1)

	op, err := provider.PingKvEx(gocbcore.PingKvOptions{}, func(services *gocbcore.PingKvResult, err error) {
		if err != nil {
			errOut = err
			signal <- true
			return
		}

		results := services.Services
		pingsOut = make([]gocbcore.PingResult, len(results))
		for pingIdx, ping := range results {
			pingsOut[pingIdx] = ping
		}
		signal <- true
	})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.sb.KvTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, timeoutError{}
	}
}

// PingOptions are the options available to the Ping operation.
type PingOptions struct {
	ServiceTypes []ServiceType
	ReportID     string
}

// Ping will ping a list of services and verify they are active and
// responding in an acceptable period of time.
//
// Volatile: This API is subject to change at any time.
func (b *Bucket) Ping(opts *PingOptions) (*PingResult, error) {
	if opts == nil {
		opts = &PingOptions{}
	}

	numServices := 0
	waitCh := make(chan error, 10)
	report := &PingResult{}
	var reportLock sync.Mutex
	services := opts.ServiceTypes

	report.ID = opts.ReportID
	if report.ID == "" {
		report.ID = uuid.New().String()
{'fraction_non_alphanumeric': 0.11319136772902673, 'fraction_numerical': 0.003287123052736887, 'mean_word_length': 3.5323834196891193, 'pattern_counts': {'":': 0, '<': 13, '<?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': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5673845', 'n_tokens_mistral': 2477, 'n_tokens_neox': 2284, 'n_words': 589}