// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package resource // import "go.opentelemetry.io/otel/sdk/resource"

import (
	
	
	
	

	
	
	
)

// Resource describes an entity about which identifying information
// and metadata is exposed.  Resource is an immutable object,
// equivalent to a map from key to unique value.
//
// Resources should be passed and stored as pointers
// (`*resource.Resource`).  The `nil` value is equivalent to an empty
// Resource.
//
// Note that the Go == operator compares not just the resource attributes but
// also all other internals of the Resource type. Therefore, Resource values
// should not be used as map or database keys. In general, the [Resource.Equal]
// method should be used instead of direct comparison with ==, since that
// method ensures the correct comparison of resource attributes, and the
// [attribute.Distinct] returned from [Resource.Equivalent] should be used for
// map and database keys instead.
type Resource struct {
	attrs     attribute.Set
	schemaURL string
}

// Compile-time check that the Resource remains comparable.
var _ map[Resource]struct{} = nil

var (
	defaultResource     *Resource
	defaultResourceOnce sync.Once
)

// ErrSchemaURLConflict is an error returned when two Resources are merged
// together that contain different, non-empty, schema URLs.
var ErrSchemaURLConflict = errors.New("conflicting Schema URL")

// New returns a [Resource] built using opts.
//
// This may return a partial Resource along with an error containing
// [ErrPartialResource] if options that provide a [Detector] are used and that
// error is returned from one or more of the Detectors. It may also return a
// merge-conflict Resource along with an error containing
// [ErrSchemaURLConflict] if merging Resources from the opts results in a
// schema URL conflict (see [Resource.Merge] for more information). It is up to
// the caller to determine if this returned Resource should be used or not
// based on these errors.
func ( context.Context,  ...Option) (*Resource, error) {
	 := config{}
	for ,  := range  {
		 = .apply()
	}

	 := &Resource{schemaURL: .schemaURL}
	return , detect(, , .detectors)
}

// NewWithAttributes creates a resource from attrs and associates the resource with a
// schema URL. If attrs contains duplicate keys, the last value will be used. If attrs
// contains any invalid items those items will be dropped. The attrs are assumed to be
// in a schema identified by schemaURL.
func ( string,  ...attribute.KeyValue) *Resource {
	 := NewSchemaless(...)
	.schemaURL = 
	return 
}

// NewSchemaless creates a resource from attrs. If attrs contains duplicate keys,
// the last value will be used. If attrs contains any invalid items those items will
// be dropped. The resource will not be associated with a schema URL. If the schema
// of the attrs is known use NewWithAttributes instead.
func ( ...attribute.KeyValue) *Resource {
	if len() == 0 {
		return &Resource{}
	}

	// Ensure attributes comply with the specification:
	// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/common/README.md#attribute
	,  := attribute.NewSetWithFiltered(, func( attribute.KeyValue) bool {
		return .Valid()
	})

	// If attrs only contains invalid entries do not allocate a new resource.
	if .Len() == 0 {
		return &Resource{}
	}

	return &Resource{attrs: } //nolint
}

// String implements the Stringer interface and provides a
// human-readable form of the resource.
//
// Avoid using this representation as the key in a map of resources,
// use Equivalent() as the key instead.
func ( *Resource) () string {
	if  == nil {
		return ""
	}
	return .attrs.Encoded(attribute.DefaultEncoder())
}

// MarshalLog is the marshaling function used by the logging system to represent this Resource.
func ( *Resource) () any {
	return struct {
		 attribute.Set
		  string
	}{
		: .attrs,
		:  .schemaURL,
	}
}

// Attributes returns a copy of attributes from the resource in a sorted order.
// To avoid allocating a new slice, use an iterator.
func ( *Resource) () []attribute.KeyValue {
	if  == nil {
		 = Empty()
	}
	return .attrs.ToSlice()
}

// SchemaURL returns the schema URL associated with Resource r.
func ( *Resource) () string {
	if  == nil {
		return ""
	}
	return .schemaURL
}

// Iter returns an iterator of the Resource attributes.
// This is ideal to use if you do not want a copy of the attributes.
func ( *Resource) () attribute.Iterator {
	if  == nil {
		 = Empty()
	}
	return .attrs.Iter()
}

// Equal reports whether r and o represent the same resource. Two resources can
// be equal even if they have different schema URLs.
//
// See the documentation on the [Resource] type for the pitfalls of using ==
// with Resource values; most code should use Equal instead.
func ( *Resource) ( *Resource) bool {
	if  == nil {
		 = Empty()
	}
	if  == nil {
		 = Empty()
	}
	return .Equivalent() == .Equivalent()
}

// Merge creates a new [Resource] by merging a and b.
//
// If there are common keys between a and b, then the value from b will
// overwrite the value from a, even if b's value is empty.
//
// The SchemaURL of the resources will be merged according to the
// [OpenTelemetry specification rules]:
//
//   - If a's schema URL is empty then the returned Resource's schema URL will
//     be set to the schema URL of b,
//   - Else if b's schema URL is empty then the returned Resource's schema URL
//     will be set to the schema URL of a,
//   - Else if the schema URLs of a and b are the same then that will be the
//     schema URL of the returned Resource,
//   - Else this is a merging error. If the resources have different,
//     non-empty, schema URLs an error containing [ErrSchemaURLConflict] will
//     be returned with the merged Resource. The merged Resource will have an
//     empty schema URL. It may be the case that some unintended attributes
//     have been overwritten or old semantic conventions persisted in the
//     returned Resource. It is up to the caller to determine if this returned
//     Resource should be used or not.
//
// [OpenTelemetry specification rules]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/resource/sdk.md#merge
func (,  *Resource) (*Resource, error) {
	if  == nil &&  == nil {
		return Empty(), nil
	}
	if  == nil {
		return , nil
	}
	if  == nil {
		return , nil
	}

	// Note: 'b' attributes will overwrite 'a' with last-value-wins in attribute.Key()
	// Meaning this is equivalent to: append(a.Attributes(), b.Attributes()...)
	 := attribute.NewMergeIterator(.Set(), .Set())
	 := make([]attribute.KeyValue, 0, .Len()+.Len())
	for .Next() {
		 = append(, .Attribute())
	}

	switch {
	case .schemaURL == "":
		return NewWithAttributes(.schemaURL, ...), nil
	case .schemaURL == "":
		return NewWithAttributes(.schemaURL, ...), nil
	case .schemaURL == .schemaURL:
		return NewWithAttributes(.schemaURL, ...), nil
	}
	// Return the merged resource with an appropriate error. It is up to
	// the user to decide if the returned resource can be used or not.
	return NewSchemaless(...), fmt.Errorf(
		"%w: %s and %s",
		ErrSchemaURLConflict,
		.schemaURL,
		.schemaURL,
	)
}

// Empty returns an instance of Resource with no attributes. It is
// equivalent to a `nil` Resource.
func () *Resource {
	return &Resource{}
}

// Default returns an instance of Resource with a default
// "service.name" and OpenTelemetrySDK attributes.
func () *Resource {
	defaultResourceOnce.Do(func() {
		var  error
		 := []Detector{
			defaultServiceNameDetector{},
			fromEnv{},
			telemetrySDK{},
		}
		if x.Resource.Enabled() {
			 = append([]Detector{defaultServiceInstanceIDDetector{}}, ...)
		}
		defaultResource,  = Detect(
			context.Background(),
			...,
		)
		if  != nil {
			otel.Handle()
		}
		// If Detect did not return a valid resource, fall back to emptyResource.
		if defaultResource == nil {
			defaultResource = &Resource{}
		}
	})
	return defaultResource
}

// Environment returns an instance of Resource with attributes
// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
func () *Resource {
	 := &fromEnv{}
	,  := .Detect(context.Background())
	if  != nil {
		otel.Handle()
	}
	return 
}

// Equivalent returns an object that can be compared for equality
// between two resources. This value is suitable for use as a key in
// a map.
func ( *Resource) () attribute.Distinct {
	return .Set().Equivalent()
}

// Set returns the equivalent *attribute.Set of this resource's attributes.
func ( *Resource) () *attribute.Set {
	if  == nil {
		 = Empty()
	}
	return &.attrs
}

// MarshalJSON encodes the resource attributes as a JSON list of { "Key":
// "...", "Value": ... } pairs in order sorted by key.
func ( *Resource) () ([]byte, error) {
	if  == nil {
		 = Empty()
	}
	return .attrs.MarshalJSON()
}

// Len returns the number of unique key-values in this Resource.
func ( *Resource) () int {
	if  == nil {
		return 0
	}
	return .attrs.Len()
}

// Encoded returns an encoded representation of the resource.
func ( *Resource) ( attribute.Encoder) string {
	if  == nil {
		return ""
	}
	return .attrs.Encoded()
}