// Copyright 2018 The Prometheus 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 promauto provides alternative constructors for the fundamental
// Prometheus metric types and their …Vec and …Func variants. The difference to
// their counterparts in the prometheus package is that the promauto
// constructors register the Collectors with a registry before returning them.
// There are two sets of constructors. The constructors in the first set are
// top-level functions, while the constructors in the other set are methods of
// the Factory type. The top-level function return Collectors registered with
// the global registry (prometheus.DefaultRegisterer), while the methods return
// Collectors registered with the registry the Factory was constructed with. All
// constructors panic if the registration fails.
//
// The following example is a complete program to create a histogram of normally
// distributed random numbers from the math/rand package:
//
//	package main
//
//	import (
//	        "math/rand"
//	        "net/http"
//
//	        "github.com/prometheus/client_golang/prometheus"
//	        "github.com/prometheus/client_golang/prometheus/promauto"
//	        "github.com/prometheus/client_golang/prometheus/promhttp"
//	)
//
//	var histogram = promauto.NewHistogram(prometheus.HistogramOpts{
//	        Name:    "random_numbers",
//	        Help:    "A histogram of normally distributed random numbers.",
//	        Buckets: prometheus.LinearBuckets(-3, .1, 61),
//	})
//
//	func Random() {
//	        for {
//	                histogram.Observe(rand.NormFloat64())
//	        }
//	}
//
//	func main() {
//	        go Random()
//	        http.Handle("/metrics", promhttp.Handler())
//	        http.ListenAndServe(":1971", nil)
//	}
//
// Prometheus's version of a minimal hello-world program:
//
//	package main
//
//	import (
//		"fmt"
//		"net/http"
//
//		"github.com/prometheus/client_golang/prometheus"
//		"github.com/prometheus/client_golang/prometheus/promauto"
//		"github.com/prometheus/client_golang/prometheus/promhttp"
//	)
//
//	func main() {
//		http.Handle("/", promhttp.InstrumentHandlerCounter(
//			promauto.NewCounterVec(
//				prometheus.CounterOpts{
//					Name: "hello_requests_total",
//					Help: "Total number of hello-world requests by HTTP code.",
//				},
//				[]string{"code"},
//			),
//			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//				fmt.Fprint(w, "Hello, world!")
//			}),
//		))
//		http.Handle("/metrics", promhttp.Handler())
//		http.ListenAndServe(":1971", nil)
//	}
//
// A Factory is created with the With(prometheus.Registerer) function, which
// enables two usage pattern. With(prometheus.Registerer) can be called once per
// line:
//
//	var (
//		reg           = prometheus.NewRegistry()
//		randomNumbers = promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
//			Name:    "random_numbers",
//			Help:    "A histogram of normally distributed random numbers.",
//			Buckets: prometheus.LinearBuckets(-3, .1, 61),
//		})
//		requestCount = promauto.With(reg).NewCounterVec(
//			prometheus.CounterOpts{
//				Name: "http_requests_total",
//				Help: "Total number of HTTP requests by status code and method.",
//			},
//			[]string{"code", "method"},
//		)
//	)
//
// Or it can be used to create a Factory once to be used multiple times:
//
//	var (
//		reg           = prometheus.NewRegistry()
//		factory       = promauto.With(reg)
//		randomNumbers = factory.NewHistogram(prometheus.HistogramOpts{
//			Name:    "random_numbers",
//			Help:    "A histogram of normally distributed random numbers.",
//			Buckets: prometheus.LinearBuckets(-3, .1, 61),
//		})
//		requestCount = factory.NewCounterVec(
//			prometheus.CounterOpts{
//				Name: "http_requests_total",
//				Help: "Total number of HTTP requests by status code and method.",
//			},
//			[]string{"code", "method"},
//		)
//	)
//
// This appears very handy. So why are these constructors locked away in a
// separate package?
//
// The main problem is that registration may fail, e.g. if a metric inconsistent
// with or equal to the newly to be registered one is already registered.
// Therefore, the Register method in the prometheus.Registerer interface returns
// an error, and the same is the case for the top-level prometheus.Register
// function that registers with the global registry. The prometheus package also
// provides MustRegister versions for both. They panic if the registration
// fails, and they clearly call this out by using the Must…  idiom. Panicking is
// problematic in this case because it doesn't just happen on input provided by
// the caller that is invalid on its own. Things are a bit more subtle here:
// Metric creation and registration tend to be spread widely over the
// codebase. It can easily happen that an incompatible metric is added to an
// unrelated part of the code, and suddenly code that used to work perfectly
// fine starts to panic (provided that the registration of the newly added
// metric happens before the registration of the previously existing
// metric). This may come as an even bigger surprise with the global registry,
// where simply importing another package can trigger a panic (if the newly
// imported package registers metrics in its init function). At least, in the
// prometheus package, creation of metrics and other collectors is separate from
// registration. You first create the metric, and then you decide explicitly if
// you want to register it with a local or the global registry, and if you want
// to handle the error or risk a panic. With the constructors in the promauto
// package, registration is automatic, and if it fails, it will always
// panic. Furthermore, the constructors will often be called in the var section
// of a file, which means that panicking will happen as a side effect of merely
// importing a package.
//
// A separate package allows conservative users to entirely ignore it. And
// whoever wants to use it, will do so explicitly, with an opportunity to read
// this warning.
//
// Enjoy promauto responsibly!
package promauto

import "github.com/prometheus/client_golang/prometheus"

// NewCounter works like the function of the same name in the prometheus package
// but it automatically registers the Counter with the
// prometheus.DefaultRegisterer. If the registration fails, NewCounter panics.
func NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
	return With(prometheus.DefaultRegisterer).NewCounter(opts)
}

// NewCounterVec works like the function of the same name in the prometheus
// package but it automatically registers the CounterVec with the
// prometheus.DefaultRegisterer. If the registration fails, NewCounterVec
// panics.
func NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec {
	return With(prometheus.DefaultRegisterer).NewCounterVec(opts, labelNames)
}

// NewCounterFunc works like the function of the same name in the prometheus
// package but it automatically registers the CounterFunc with the
// prometheus.DefaultRegisterer. If the registration fails, NewCounterFunc
// panics.
func NewCounterFunc(opts prometheus.CounterOpts, function func() float64) prometheus.CounterFunc {
	return With(prometheus.DefaultRegisterer).NewCounterFunc(opts, function)
}

// NewGauge works like the function of the same name in the prometheus package
// but it automatically registers the Gauge with the
// prometheus.DefaultRegisterer. If the registration fails, NewGauge panics.
func NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
	return With(prometheus.DefaultRegisterer).NewGauge(opts)
}

// NewGaugeVec works like the function of the same name in the prometheus
// package but it automatically registers the GaugeVec with the
// prometheus.DefaultRegisterer. If the registration fails, NewGaugeVec panics.
func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
	return With(prometheus.DefaultRegisterer).NewGaugeVec(opts, labelNames)
}

// NewGaugeFunc works like the function of the same name in the prometheus
// package but it automatically registers the GaugeFunc with the
// prometheus.DefaultRegisterer. If the registration fails, NewGaugeFunc panics.
func NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc {
	return With(prometheus.DefaultRegisterer).NewGaugeFunc(opts, function)
}

// NewSummary works like the function of the same name in the prometheus package
// but it automatically registers the Summary with the
// prometheus.DefaultRegisterer. If the registration fails, NewSummary panics.
func NewSummary(opts prometheus.SummaryOpts) prometheus.Summary {
	return With(prometheus.DefaultRegisterer).NewSummary(opts)
}

// NewSummaryVec works like the function of the same name in the prometheus
// package but it automatically registers the SummaryVec with the
// prometheus.DefaultRegisterer. If the registration fails, NewSummaryVec
// panics.
func NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec {
	return With(prometheus.DefaultRegisterer).NewSummaryVec(opts, labelNames)
}

// NewHistogram works like the function of the same name in the prometheus
// package but it automatically registers the Histogram with the
// prometheus.DefaultRegisterer. If the registration fails, NewHistogram panics.
func NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram {
	return With(prometheus.DefaultRegisterer).NewHistogram(opts)
}

// NewHistogramVec works like the function of the same name in the prometheus
// package but it automatically registers the HistogramVec with the
// prometheus.DefaultRegisterer. If the registration fails, NewHistogramVec
// panics.
func NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
	return With(prometheus.DefaultRegisterer).NewHistogramVec(opts, labelNames)
}

// NewUntypedFunc works like the function of the same name in the prometheus
// package but it automatically registers the UntypedFunc with the
// prometheus.DefaultRegisterer. If the registration fails, NewUntypedFunc
// panics.
func NewUntypedFunc(opts prometheus.UntypedOpts, function func() float64) prometheus.UntypedFunc {
	return With(prometheus.DefaultRegisterer).NewUntypedFunc(opts, function)
}

// Factory provides factory methods to create Collectors that are automatically
// registered with a Registerer. Create a Factory with the With function,
// providing a Registerer to auto-register created Collectors with. The zero
// value of a Factory creates Collectors that are not registered with any
// Registerer. All methods of the Factory panic if the registration fails.
type Factory struct {
	r prometheus.Registerer
}

// With creates a Factory using the provided Registerer for registration of the
// created Collectors. If the provided Registerer is nil, the returned Factory
// creates Collectors that are not registered with any Registerer.
func With(r prometheus.Registerer) Factory { return Factory{r} }

// NewCounter works like the function of the same name in the prometheus package
// but it automatically registers the Counter with the Factory's Registerer.
func (f Factory) NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
	c := prometheus.NewCounter(opts)
	if f.r != nil {
		f.r.MustRegister(c)
	}
	return c
}

// NewCounterVec works like the function of the same name in the prometheus
// package but it automatically registers the CounterVec with the Factory's
// Registerer.
func (f Factory) NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec {
	c := prometheus.NewCounterVec(opts, labelNames)
	if f.r != nil {
		f.r.MustRegister(c)
	}
	return c
}

// NewCounterFunc works like the function of the same name in the prometheus
// package but it automatically registers the CounterFunc with the Factory's
// Registerer.
func (f Factory) NewCounterFunc(opts prometheus.CounterOpts, function func() float64) prometheus.CounterFunc {
	c := prometheus.NewCounterFunc(opts, function)
	if f.r != nil {
		f.r.MustRegister(c)
	}
	return c
}

// NewGauge works like the function of the same name in the prometheus package
// but it automatically registers the Gauge with the Factory's Registerer.
func (f Factory) NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
	g := prometheus.NewGauge(opts)
	if f.r != nil {
		f.r.MustRegister(g)
	}
	return g
}

// NewGaugeVec works like the function of the same name in the prometheus
// package but it automatically registers the GaugeVec with the Factory's
// Registerer.
func (f Factory) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
	g := prometheus.NewGaugeVec(opts, labelNames)
	if f.r != nil {
		f.r.MustRegister(g)
	}
	return g
}

// NewGaugeFunc works like the function of the same name in the prometheus
// package but it automatically registers the GaugeFunc with the Factory's
// Registerer.
func (f Factory) NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc {
	g := prometheus.NewGaugeFunc(opts, function)
	if f.r != nil {
		f.r.MustRegister(g)
	}
	return g
}

// NewSummary works like the function of the same name in the prometheus package
// but it automatically registers the Summary with the Factory's Registerer.
func (f Factory) NewSummary(opts prometheus.SummaryOpts) prometheus.Summary {
	s := prometheus.NewSummary(opts)
	if f.r != nil {
		f.r.MustRegister(s)
	}
	return s
}

// NewSummaryVec works like the function of the same name in the prometheus
// package but it automatically registers the SummaryVec with the Factory's
// Registerer.
func (f Factory) NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec {
	s := prometheus.NewSummaryVec(opts, labelNames)
	if f.r != nil {
		f.r.MustRegister(s)
	}
	return s
}

// NewHistogram works like the function of the same name in the prometheus
// package but it automatically registers the Histogram with the Factory's
// Registerer.
func (f Factory) NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram {
	h := prometheus.NewHistogram(opts)
	if f.r != nil {
		f.r.MustRegister(h)
	}
	return h
}

// NewHistogramVec works like the function of the same name in the prometheus
// package but it automatically registers the HistogramVec with the Factory's
// Registerer.
func (f Factory) NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
	h := prometheus.NewHistogramVec(opts, labelNames)
	if f.r != nil {
		f.r.MustRegister(h)
	}
	return h
}

// NewUntypedFunc works like the function of the same name in the prometheus
// package but it automatically registers the UntypedFunc with the Factory's
// Registerer.
func (f Factory) NewUntypedFunc(opts prometheus.UntypedOpts, function func() float64) prometheus.UntypedFunc {
	u := prometheus.NewUntypedFunc(opts, function)
	if f.r != nil {
		f.r.MustRegister(u)
	}
	return u
}
