= SprigX
Brent Saner <bts@square-r00t.net>
Last rendered {localdatetime}
:doctype: book
:docinfo: shared
:data-uri:
:imagesdir: images
:sectlinks:
:sectnums:
:sectnumlevels: 7
:toc: preamble
:toc2: left
:idprefix:
:toclevels: 7
:source-highlighter: rouge
:docinfo: shared

[id="wat"]
== What is SprigX?
SprigX are extensions to https://masterminds.github.io/sprig/[the `sprig` library^] (https://pkg.go.dev/github.com/Masterminds/sprig/v3[Go docs^]).

They provide functions that offer more enriched use cases and domain-specific data.

[TIP]
====
If you are reading this README on the Go Module Directory documentation (https://pkg.go.dev/r00t2.io/goutils/tplx/sprigx)
or the directory landing page (https://git.r00t2.io/r00t2/go_goutils/src/branch/master/tplx/sprigx), it may not render correctly.

Be sure to view it at properly via https://git.r00t2.io/r00t2/go_goutils/src/branch/master/tplx/sprigx/README.adoc[the AsciiDoc rendering^]
or by downloading and viewing the https://git.r00t2.io/r00t2/go_goutils/raw/branch/master/tplx/sprigx/README.html[HTML version^].
====

[id="use"]
== How do I Use SprigX?

The same way you would `sprig`!

[%collapsible]
.Like this.
====
[source,go]
----
package main

import (
	htmlTplLib "html/template"
	txtTplLib "text/template"

	"r00t2.io/goutils/tplx/sprigx"
)

var (
	txtTpl *txtTplLib.Template = txtTplLib.
		New("").
		Funcs(
			sprigx.TxtFuncMap(),
		)
	htmlTpl *htmlTplLib.Template = htmlTplLib.
		New("").
		Funcs(
			sprigx.HtmlFuncMap(),
		)
)
----
====

They can even be combined/used together.

[%collapsible]
.Like this.
====
[source,go]
----
package main

import (
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"r00t2.io/goutils/tplx/sprigx"
)

var txtTpl *template.Template = template.
		New("").
		Funcs(
			sprigx.TxtFuncMap(),
		).
		Funcs(
			sprig.TxtFuncMap(),
		)
// Or:
/*
var txtTpl *template.Template = template.
		New("").
		Funcs(
			sprig.TxtFuncMap(),
		).
		Funcs(
			sprigx.TxtFuncMap(),
		)
 */
----
====

If a `<template>.FuncMap` is added via `.Funcs()` *after* template parsing, it will override any functions of the same name of a `<template>.FuncMap` *before* parsing.

For example, if both `sprig` and `sprigx` provide a function `foo`:

this will use `foo` from `sprigx`

[%collapsible]
.(show)
====
[source,go]
----
package main

import (
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"r00t2.io/goutils/tplx/sprigx"
)

const (
	myTpl string = `{{ "This is an example template string." | foo }}`
)

var (
	tpl *template.Template = template.Must(
					template.
						New("").
						Funcs(sprig.TxtFuncMap()).
						Parse(myTpl),
				).
				Funcs(sprigx.TxtFuncMap())
)
----
====

whereas this will use `foo` from `sprig`

[%collapsible]
.(show)
====
[source,go]
----
package main

import (
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"r00t2.io/goutils/tplx/sprigx"
)

const (
	myTpl string = `{{ "This is an example template string." | foo }}`
)

var (
	tpl *template.Template = template.Must(
					template.
						New("").
						Funcs(sprigx.TxtFuncMap()).
						Parse(myTpl),
					).
					Funcs(sprig.TxtFuncMap())
)
----
====

and a function can even be explicitly [[override]]overridden.

[%collapsible]
.(show)
====
This would override a function `foo` and `foo2` in `sprigx` from `foo` and `foo2` from `sprig`, but leave all other `sprig` functions untouched.

[source,go]
----
package main

import (
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"r00t2.io/goutils/tplx/sprigx"
)

const (
	myTpl string = `{{ "This is an example template string." | foo }}`
)

var (
	overrideFuncs template.FuncMap = sprig.TxtFuncMap()
	tpl *template.Template = template.Must(
					template.
						New("").
						Funcs(sprigx.TxtFuncMap()).
						Parse(myTpl),
					).
					Funcs(
						template.FuncMap(
							map[string]any{
								"foo": overrideFuncs["foo"],
								"foo2": overrideFuncs["foo2"],
							},
						),
					)
)
----
====

[id="fn"]
== Functions
Expect this list to grow over time, and potentially more frequently than the `sprigx` functions.

Each function includes its function signature to indicate what types of arguments/parameters it accepts,
what it returns, and if it may potentially return an error or not.

[id="fn_dbg"]
=== Debugging

[id="fn_dbg_dump"]
==== `dump`
[source,go]
.Function Signature
----
func dump(a ...interface{}) (out string)
----

The `dump` function directly calls https://pkg.go.dev/github.com/davecgh/go-spew/spew#Sdump[`github.com/davecgh/go-spew/spew.Sdump`^]
for whatever object(s) is/are passed to it.

[id="fn_meta"]
=== "Meta"/Template Helpers

[id="fn_meta_isnil"]
==== `metaIsNil`
[source,go]
.Function Signature
----
func metaIsNil(obj any) (isNil bool)
----

`metaIsNil` returns `true` if `obj` is explicitly nil, otherwise it returns false.

This function fills in the gap that https://pkg.go.dev/text/template#IsTrue[`text/template.IsTrue`^] and https://pkg.go.dev/html/template#IsTrue[`html/template.IsTrue`^] (expressed in templates as `{{ if ... }}`) leaves, as those functions/expressions return false for e.g. `false` booleans AND nils.

[id="fn_meta_nop"]
==== `Nop`
[source,go]
.Function Signature
----
func Nop(obj ...any) (s string)
----

`Nop` is not actually a *template* function, but rather an exported *Go* function that one can use in
an <<override, override map>> to explicitly disable certain template functions that may be deemed "unsafe" and/or to sanitize templates from
untrusted input.

It will *never* error or panic, and `s` is *always* an empty string.

[id="fn_os"]
=== Operating System

[id="fn_os_fqdn"]
==== `osFQDN`
[source,go]
.Function Signature
----
func osFQDN() (fqdn string, err error)
----

`osFQDN` currently just directly calls https://pkg.go.dev/os#Hostname[`os.Hostname`^].

As such, it comes with the same caveats -- namely that it isn't guaranteed to be
an FQDN, it will be precisely/exactly whatever the kernel/OS hostname is set as.

In the future, it may be extended to support a more diligent effort to determine
an actual FQDN, and return an error if it is unable to be derived.

To (relatively) predictably get the "short hostname", use <<fn_os_hst>>.
To directly/predictably use https://pkg.go.dev/os#Hostname[`os.Hostname`^], use <<fn_os_hstnm>>.

[id="fn_os_grpid"]
==== `osGroupById`
[source,go]
.Function Signature
----
func osGroupById[T string | int](gid T) (g *user.Group, err error)
----

`osGroupById` returns an https://pkg.go.dev/os/user#Group[`os/user.Group`^] from a given group ID/GID.

It more or less behaves exactly like https://pkg.go.dev/os/user#LookupGroupId[`os/user.LookupGroupId`^],
except it will accept either a `string` *or* an `int` as the GID.

[id="fn_os_grpnm"]
==== `osGroupByName`
[source,go]
.Function Signature
----
func osGroupByName(grpNm string) (g *user.Group, err error)
----

`osGroupByName` returns an https://pkg.go.dev/os/user#Group[`os/user.Group`^] from a given group name.

It behaves exactly like https://pkg.go.dev/os/user#LookupGroup[`os/user.LookupGroup`^].

[id="fn_os_hst"]
==== `osHost`
[source,go]
.Function Signature
----
func osHost() (out string, err error)
----

`osHost` returns the "short hostname" by calling https://pkg.go.dev/os#Hostname[`os.Hostname`^]
and returning the first "host label" (as RFCs refer to it).

e.g.:

[source,gotemplate]
----
{{- $fqdn := osFQDN -}}
{{- $h := osHost -}}
{{- $cmp := index ($fqdn | splitList ".") 0 -}}
osHost {{ $h }} should be equal to first label of FQDN {{ $cmp }}.
----

[TIP]
====
The `splitList` function shown aboce is from the https://masterminds.github.io/sprig/string_slice.html[`sprig` string slice functions^].
====

To (try to) get the FQDN, use <<fn_os_fqdn>>.
To directly use https://pkg.go.dev/os#Hostname[`os.Hostname`^], use <<fn_os_hstnm>>.

[id="fn_os_hstnm"]
==== `osHostname`
[source,go]
.Function Signature
----
func osHostname() (out string, err error)
----

`osHostname` directly calls https://pkg.go.dev/os#Hostname[`os.Hostname`^].

[id="fn_os_idst"]
==== `osIdState`
[source,go]
.Function Signature
----
func osIdState() (idst sysutils.IDState)
----

`osIdState` returns the current runtime process' https://pkg.go.dev/r00t2.io/sysutils#IDState[`r00t2.io/sysutils.IDState`^].

It directly calls https://pkg.go.dev/r00t2.io/sysutils#GetIDState[`r00t2.io/sysutils.GetIDState`^].

[WARNING]
====
This is more or less useless on Windows; it returns only a dummy struct for cross-platform compatibility.
====

[id="fn_os_usr"]
==== `osUser`
[source,go]
.Function Signature
----
func osUser() (u *user.User, err error)
----

`osUser` returns the current runtime process' https://pkg.go.dev/os/user#User[`os/user.User`^].

It directly calls https://pkg.go.dev/os/user#Current[`os/user.Current`^].

[id="fn_os_usrid"]
==== `osUserById`
[source,go]
.Function Signature
----
func osUserById[T string | int](uid T) (u *user.User, err error)
----

`osUserById` returns an https://pkg.go.dev/os/user#User[`os/user.User`^] from a given user ID/UID.

It more or less behaves exactly like https://pkg.go.dev/os/user#LookupId[`os/user.LookupId`^],
except it will accept either a `string` *or* an `int` as the UID.

[id="fn_os_usrnm"]
==== `osUserByName`
[source,go]
.Function Signature
----
func osUserByName(userNm string) (u *user.User, err error)
----

`osUserByName` returns an https://pkg.go.dev/os/user#User[`os/user.User`^] from a given username.

It directly calls https://pkg.go.dev/os/user#Lookup[`os/user.Lookup`^].

[id="fn_path"]
=== Paths

[id="fn_path_gnrc"]
==== Generic
These operate similar to https://pkg.go.dev/path[the `path` stdlib library^] and use a fixed `/` path separator.

[id="fn_path_gnrc_pj"]
===== `pathJoin`
[source,go]
.Function Signature
----
func pathJoin(elem ...string) (out string)
----

`pathJoin` directly calls https://pkg.go.dev/path#Join[`path.Join`^].

[WARNING]
====
If you are joining paths in a pipeline, you almost assuredly want <<fn_path_gnrc_ppj>> or <<fn_path_gnrc_pspj>> instead
unless you are explicitly *appending* a pipeline result to a path.
====

[source,gotemplate]
----
{{- pathJoin "a" "b" "c" }}
{{- pathJoin "/" "a" "b" "c" }}
{{- pathJoin "/a/b" "c" }}
----

renders as:

[source,text]
----
a/b/c
/a/b/c
/a/b/c
----

[id="fn_path_gnrc_ppj"]
===== `pathPipeJoin`
[source,go]
.Function Signature
----
func pathPipeJoin(elems ...string) (out string)
----

`pathPipeJoin` operates like <<fn_path_gnrc_pj>> with one deviation: the root/base path is expected to be *last* in the arguments.

This makes it much more suitable for use in template pipelines, as the previous value in a pipeline is passed in
as the last element to the next pipe function.

[source,gotemplate]
----
{{- $myBase := "/a" -}}
{{- pathPipeJoin "b" "c" "a" }}
{{- pathPipeJoin "a" "b" "c" "/" }}
{{- $myBase | pathPipeJoin "b" "c" }}
----

renders as:

[source,text]
----
a/b/c
/a/b/c
/a/b/c
----

[id="fn_path_gnrc_psj"]
===== `pathSliceJoin`
[source,go]
.Function Signature
----
func pathSliceJoin(sl []string) (out string)
----

`pathSliceJoin` joins a slice of path segment strings (`[]string`) instead of a variadic sequence of strings.

[TIP]
====
The `splitList` function shown below is from the https://masterminds.github.io/sprig/string_slice.html[`sprig` string slice functions^].
====

[source,gotemplate]
----
{{- $myList := "a,b,c" | splitList "," -}}
{{- $myList | pathSliceJoin }}
{{- ("a,b,c" | splitList ",") | pathSliceJoin }}
{{- ("/,a,b,c" | splitList ",") | pathSliceJoin }}
----

renders as:

[source,text]
----
a/b/c
a/b/c
/a/b/c
----

[id="fn_path_gnrc_pspj"]
===== `pathSlicePipeJoin`
[source,go]
.Function Signature
----
func pathSlicePipeJoin(sl []string, root string) (out string)
----

`pathSlicePipeJoin` operates like <<fn_path_gnrc_ppj>> in that it is suitable for pipeline use in which the root/base path is passed in
from the pipeline, but it is like <<fn_path_gnrc_psj>> in that it then also accepts a slice of
path segments (`[]string`) to append to that base path.

[TIP]
====
The `splitList` function shown below is from the https://masterminds.github.io/sprig/string_slice.html[`sprig` string slice functions^].
====

[source,gotemplate]
----
{{- $myBase := "/a" -}}
{{- $myList := "b,c,d" | splitList "." -}}
{{- pathSlicePipeJoin $myList $myBase }}
{{- $myBase | pathSlicePipeJoin $myList }}
----

renders as:

[source,text]
----
/a/b/c
/a/b/c
----

[id="fn_path_gnrc_psubj"]
===== `pathSubJoin`
[source,go]
.Function Signature
----
func pathSubJoin(root string, elems ...string) (out string)
----

`pathSubJoin` operates like <<fn_path_gnrc_pj>> but it expects an explicit root/base path.

The pipeline-friendly equivalent of this is <<fn_path_gnrc_ppj>>.

[source,gotemplate]
----
{{- pathSubJoin "/a/b" "c" }}
{{- pathSubJoin "/" "a" "b" "c" }}
{{- "c" | pathSubJoin "/" "a" "b" }}
----

renders as:

[source,text]
----
/a/b/c
/a/b/c
/a/b/c
----

[id="fn_path_os"]
==== OS/Platform-Tailored
These operate similar to https://pkg.go.dev/path/filepath[the `path/filepath` stdlib library^], and use the OS-specific https://pkg.go.dev/os#PathSeparator[`os.PathSeparator`^].

[WARNING]
====
Take special note of the oddness around specifying Windows paths and drive letters in e.g. <<fn_path_os_pj>>!

It is recommended to make use of <<fn_sys_os>> to conditionally format path bases/roots if needed.
====

[id="fn_path_os_pj"]
===== `osPathJoin`
[source,go]
.Function Signature
----
func osPathJoin(elem ...string) (out string)
----

`osPathJoin` directly calls https://pkg.go.dev/path/filepath#Join[`path/filepath.Join`^].

[WARNING]
====
If you are joining paths in a pipeline, you almost assuredly want <<fn_path_os_ppj>> or <<fn_path_os_pspj>> instead unless you are
explicitly *appending* a pipeline result to a path.
====

[source,gotemplate]
----
{{- osPathJoin "a" "b" "c" }}
{{- osPathJoin "/" "a" "b" "c" }}
{{- osPathJoin "C:\\" "a" "b" "c" }}
{{- osPathJoin "C:" "a" "b" "c" }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
a\b\c
\a\b\c
\a\b\c
C:\a\b\c
C:a\b\c
----
| Others (e.g. Linux, macOS) | [source,text]
----
a/b/c
/a/b/c
C:\/a/b/c
C:/a/b/c
----
|===

[id="fn_path_os_ppj"]
===== `osPathPipeJoin`
[source,go]
.Function Signature
----
func osPathPipeJoin(elems ...string) (out string)
----

`osPathPipeJoin` operates like <<fn_path_gnrc_ppj>> (except using OS-specific path separators).

This makes it much more suitable for use in template pipelines, as the previous value in a pipeline is passed in
as the last argument to the next pipe function.

[source,gotemplate]
----
{{- $myBase := "/a" -}}
{{- osPathPipeJoin "b" "c" "a" }}
{{- osPathPipeJoin "a" "b" "c" "/" }}
{{- $myBase | osPathPipeJoin "b" "c" }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
a\b\c
\a\b\c
\a\b\c
----
| Others (e.g. Linux, macOS) | [source,text]
----
a/b/c
/a/b/c
/a/b/c
----
|===

[id="fn_path_ossep"]
===== `osPathSep`
[source,go]
.Function Signature
----
func osPathSep() (out string)
----

`osPathSep` returns the https://pkg.go.dev/os#PathSeparator[`os.PathSeparator`^] for this OS.

[source,gotemplate]
----
{{- osPathSep }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
\
----
| Others (e.g. Linux, macOS) | [source,text]
----
/
----
|===

[id="fn_path_os_psj"]
===== `osPathSliceJoin`
[source,go]
.Function Signature
----
func osPathSliceJoin(sl []string) (out string)
----

`osPathSliceJoin` operates like <<fn_path_gnrc_psj>> but with OS-specific path separators.

[TIP]
====
The `splitList` function shown below is from the https://masterminds.github.io/sprig/string_slice.html[`sprig` string slice functions^].
====

[source,gotemplate]
----
{{- $myList := "a,b,c" | splitList "," -}}
{{- $myList | osPathSliceJoin }}
{{- ("a,b,c" | splitList ",") | osPathSliceJoin }}
{{- ("/,a,b,c" | splitList ",") | osPathSliceJoin }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
a\b\c
a\b\c
\a\b\c
----
| Others (e.g. Linux, macOS) | [source,text]
----
a/b/c
a/b/c
/a/b/c
----
|===

[id="fn_path_os_pspj"]
===== `osPathSlicePipeJoin`
[source,go]
.Function Signature
----
func osPathSlicePipeJoin(sl []string, root string) (out string)
----

`osPathSlicePipeJoin` operates like <<fn_path_gnrc_pspj>> but with OS-specific separators.

[TIP]
====
The `splitList` function shown below is from the https://masterminds.github.io/sprig/string_slice.html[`sprig` string slice functions^].
====

[source,gotemplate]
----
{{- $myBase := "/a" -}}
{{- $myList := "b,c,d" | splitList "." -}}
{{- osPathSlicePipeJoin $myList $myBase }}
{{- $myBase | osPathSlicePipeJoin $myList }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
\a\b\c\d
\a\b\c\d
----
| Others (e.g. Linux, macOS) | [source,text]
----
/a/b/c/d
/a/b/c/d
----
|===

[id="fn_path_os_psubj"]
===== `osPathSubJoin`
[source,go]
.Function Signature
----
func osPathSubJoin(root string, elems ...string) (out string)
----

`osPathSubJoin` operates like <<fn_path_gnrc_psubj>> but with OS-specific separators.

The pipeline-friendly equivalent of this is <<fn_path_os_ppj>>.

[source,gotemplate]
----
{{- osPathSubJoin "/a/b" "c" }}
{{- osPathSubJoin "/" "a" "b" "c" }}
{{- "c" | osPathSubJoin "/" "a" "b" }}
----

renders as:

[cols="^.^2,.^4a",options="header"]
|===
| OS ^| Result

| Windows | [source,text]
----
\a\b\c
\a\b\c
\a\b\c
----
| Others (e.g. Linux, macOS) | [source,text]
----
/a/b/c
/a/b/c
/a/b/c
----
|===

[id="fn_ps"]
=== PSUtils
These are functions from https://pkg.go.dev/github.com/shirou/gopsutil/v4[`github.com/shirou/gopsutil/v4`^] packages.

[id="fn_ps_cpu"]
==== CPU/Processor

[id="fn_ps_cpu_cnts"]
===== `psCpuCnts`
[source,go]
.Function Signature
----
func psCpuCnts(logical bool) (numCpu int, err error)
----

`psCpuCnts` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/cpu#Counts[`github.com/shirou/gopsutil/v4/cpu.Counts`^].

[id="fn_ps_cpu_info"]
===== `psCpuInfo`
[source,go]
.Function Signature
----
func psCpuInfo() (cpuInfo [](github.com/shirou/gopsutil/v4/cpu).Info, err error)
----

`psCpuInfo` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/cpu#Info[`github.com/shirou/gopsutil/v4/cpu.Info`^].

[id="fn_ps_cpu_pct"]
===== `psCpuPct`
[source,go]
.Function Signature
----
func psCpuPct(interval time.Duration, percpu bool) (pcts []float64, err error)
----

`psCpuPct` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/cpu#Percent[`github.com/shirou/gopsutil/v4/cpu.Percent`^].

[id="fn_ps_cpu_tms"]
===== `psCpuTimes`
[source,go]
.Function Signature
----
func psCpuTimes(percpu bool) (cpuTimes []TimesStat, err error)
----

`psCpuTimes` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/cpu#Times[`github.com/shirou/gopsutil/v4/cpu.Times`^].

[id="fn_ps_dsk"]
==== Disk

[id="fn_ps_dsk_iocnts"]
===== `psDiskIoCnts`
[source,go]
.Function Signature
----
func psDiskIoCnts(names ...string) (stats map[string]IOCountersStat, err error)
----

`psDiskIoCnts` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/disk#IOCounters[`github.com/shirou/gopsutil/v4/disk.IOCounters`^].

[id="fn_ps_dsk_lbl"]
===== `psDiskLabel`
[source,go]
.Function Signature
----
func psDiskLabel(name string) (label string, err error)
----

`psDiskLabel` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/disk#Label[`github.com/shirou/gopsutil/v4/disk.Label`^].

[id="fn_ps_dsk_parts"]
===== `psDiskParts`
[source,go]
.Function Signature
----
func psDiskParts(all bool) (parts [](github.com/shirou/gopsutil/v4/disk).PartitionStat, err error)
----

`psDiskParts` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/disk#Partitions[`github.com/shirou/gopsutil/v4/disk.Partitions`^].

[id="fn_ps_dsk_srl"]
===== `psDiskSerial`
[source,go]
.Function Signature
----
func psDiskSerial(name string) (serial string, err error)
----

`psDiskSerial` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/disk#SerialNumber[`github.com/shirou/gopsutil/v4/disk.SerialNumber`^].

[id="fn_ps_dsk_usg"]
===== `psDiskUsage`
[source,go]
.Function Signature
----
func psDiskUsage(path string) (usage *(github.com/shirou/gopsutil/v4/disk).UsageStat, err error)
----

`psDiskUsage` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/disk#Usage[`github.com/shirou/gopsutil/v4/disk.Usage`^].

[id="fn_ps_hst"]
==== Host

[id="fn_ps_hst_boot"]
===== `psHostBoot`
[source,go]
.Function Signature
----
func psHostBoot() (bootEpoch uint64, err error)
----

`psHostBoot` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#BootTime[`github.com/shirou/gopsutil/v4/host.BootTime`^].

[id="fn_ps_hst_id"]
===== `psHostId`
[source,go]
.Function Signature
----
func psHostId() (hostId string, err error)
----

`psHostId` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#HostID[`github.com/shirou/gopsutil/v4/host.HostID`^].

[id="fn_ps_hst_info"]
===== `psHostInfo`
[source,go]
.Function Signature
----
func psHostInfo() (info *(github.com/shirou/gopsutil/v4/host).InfoStat, err error)
----

`psHostInfo` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#Info[`github.com/shirou/gopsutil/v4/host.Info`^].

[id="fn_ps_hst_krnarch"]
===== `psHostKernArch`
[source,go]
.Function Signature
----
func psHostKernArch() (arch string, err error)
----

`psHostKernArch` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#KernelArch[`github.com/shirou/gopsutil/v4/host.KernelArch`^].

[id="fn_ps_hst_krnver"]
===== `psHostKernVer`
[source,go]
.Function Signature
----
func psHostKernVer() (ver string, err error)
----

`psHostKernVer` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#KernelVersion[`github.com/shirou/gopsutil/v4/host.KernelVersion`^].

[id="fn_ps_hst_plat"]
===== `psHostPlatInfo`
[source,go]
.Function Signature
----
func psHostPlatInfo() (platInfo [3]string, err error)
----

`psHostPlatInfo` wraps https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#PlatformInformation[`github.com/shirou/gopsutil/v4/host.PlatformInformation`^].

It is necessary to wrap because the function normally returns `(string, string, string, error)` but a template function may only return either a single value (of any type) or a single value of any type and an error, so the three `string` returns are consolidated into an ordered `[3]string`.

[id="fn_ps_hst_uptm"]
===== `psHostPlatUptime`
[source,go]
.Function Signature
----
func psHostPlatUptime() (uptimeSecs uint64, err error)
----

`psHostPlatUptime` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#Uptime[`github.com/shirou/gopsutil/v4/host.Uptime`^].

[id="fn_ps_hst_usrs"]
===== `psHostUsers`
[source,go]
.Function Signature
----
func psHostUsers() (users [](github.com/shirou/gopsutil/v4/host).UserStat, err error)
----

`psHostUsers` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#Users[`github.com/shirou/gopsutil/v4/host.Users`^].

[id="fn_ps_hst_virt"]
===== `psHostPlatVirt`
[source,go]
.Function Signature
----
func psHostVirt() (virtInfo [2]string, err error)
----

`psHostPlatVirt` wraps https://pkg.go.dev/github.com/shirou/gopsutil/v4/host#Virtualization[`github.com/shirou/gopsutil/v4/host.Virtualization`^].

It is necessary to wrap because the function normally returns `(string, string, error)` but a template function may only return either a single value (of any type) or a single value of any type and an error, so the two `string` returns are consolidated into an ordered `[2]string`.

[id="fn_ps_ld"]
==== Load

[id="fn_ps_ld_avg"]
===== `psLoadAvg`
[source,go]
.Function Signature
----
func psLoadAvg() (avg *(github.com/shirou/gopsutil/v4/load).AvgStat, err error)
----

`psLoadAvg` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/load#Avg[`github.com/shirou/gopsutil/v4/load.Avg`^].

[id="fn_ps_ld_misc"]
===== `psLoadMisc`
[source,go]
.Function Signature
----
func psLoadMisc() (misc *(github.com/shirou/gopsutil/v4/load).MiscStat, err error)
----

`psLoadMisc` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/load#Misc[`github.com/shirou/gopsutil/v4/load.Misc`^].

[id="fn_ps_mem"]
==== Memory

[id="fn_ps_mem_exvmem"]
===== `psMemExVMem`
[source,go]
.Function Signature
----
func psMemExVMem() (exVMem *(github.com/shirou/gopsutil/v4/mem).ExVirtualMemory, err error)
----

[WARNING]
====
This function is available on Windows and Linux platforms *only*.
====

[WARNING]
====
This function returns very different types depending on platform.

* Linux: https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=linux#ExVirtualMemory[`mem.ExVirtualMemory`^]
* Windows: https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=windows#ExVirtualMemory[`mem.ExVirtualMemory`^]
====

This function wraps link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=linux#NewExLinux[`github.com/shirou/gopsutil/v4/mem.NewExLinux`^].link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=linux#ExLinux.VirtualMemory[`VirtualMemory`^] on Linux and
link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=windows#NewExWindows[`github.com/shirou/gopsutil/v4/mem.NewExWindows`^].link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem?GOOS=windows#ExWindows.VirtualMemory[`VirtualMemory`^] on Windows.

[id="fn_ps_mem_swap"]
===== `psMemSwap`
[source,go]
.Function Signature
----
func psMemSwap() (swap *(github.com/shirou/gopsutil/v4/mem).SwapMemoryStat, err error)
----

`psMemSwap` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem#SwapMemory[`github.com/shirou/gopsutil/v4/mem.SwapMemory`^].

[id="fn_ps_mem_swapdevs"]
===== `psMemSwapDevs`
[source,go]
.Function Signature
----
func psMemSwapDevs() (swapDevs []*(github.com/shirou/gopsutil/v4/mem).SwapDevice, err error)
----

`psMemSwapDevs` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem#SwapDevices[`github.com/shirou/gopsutil/v4/mem.SwapDevices`^].

[id="fn_ps_mem_vmem"]
===== `psMemVMem`
[source,go]
.Function Signature
----
func psMemVMem() (vmem *(github.com/shirou/gopsutil/v4/mem).VirtualMemoryStat, err error)
----

`psMemVMem` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/mem#VirtualMemory[`github.com/shirou/gopsutil/v4/mem.VirtualMemory`^].

[id="fn_ps_net"]
==== Network

[id="fn_ps_net_conns"]
===== `psNetConns`
[source,go]
.Function Signature
----
func psNetConns(kind string) (conns [](github.com/shirou/gopsutil/v4/net).ConnectionStat, err error)
----

`psNetConns` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#Connections[`github.com/shirou/gopsutil/v4/net.Connections`^].

[id="fn_ps_net_connsmax"]
===== `psNetConnsMax`
[source,go]
.Function Signature
----
func psNetConnsMax(kind string, maxConn int) (conns [](github.com/shirou/gopsutil/v4/net).ConnectionStat, err error)
----

`psNetConnsMax` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ConnectionsMax[`github.com/shirou/gopsutil/v4/net.ConnectionsMax`^].

[id="fn_ps_net_connspid"]
===== `psNetConnsPid`
[source,go]
.Function Signature
----
func psNetConnsPid(kind string, pid int32) (conns [](github.com/shirou/gopsutil/v4/net).ConnectionStat, err error)
----

`psNetConnsPid` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ConnectionsPid[`github.com/shirou/gopsutil/v4/net.ConnectionsPid`^].

[id="fn_ps_net_connspidmax"]
===== `psNetConnsPidMax`
[source,go]
.Function Signature
----
func psNetConnsPidMax(kind string, pid int32, maxConn int) (conns [](github.com/shirou/gopsutil/v4/net).ConnectionStat, err error)
----

`psNetConnsPidMax` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ConnectionsPidMax[`github.com/shirou/gopsutil/v4/net.ConnectionsPidMax`^].

[id="fn_ps_net_ct"]
===== `psNetCTStats`
[source,go]
.Function Signature
----
func psNetCTStats(percCpu bool) (ctStats [](github.com/shirou/gopsutil/v4/net).ConntrackStat, err error)
----

`psNetCTStats` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ConntrackStats[`github.com/shirou/gopsutil/v4/net.ConntrackStats`^].

[id="fn_ps_net_ctlist"]
===== `psNetCTStatList`
[source,go]
.Function Signature
----
func psNetCTStatList() (ctStats *(github.com/shirou/gopsutil/v4/net).ConntrackStatList, err error)
----

`psNetCTStatList` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ConntrackStatList[`github.com/shirou/gopsutil/v4/net.ConntrackStatList`^].

[id="fn_ps_net_fltcnt"]
===== `psNetFilterCnts`
[source,go]
.Function Signature
----
func psNetFilterCnts() (filterCnts [](github.com/shirou/gopsutil/v4/net).FilterStat, err error)
----

`psNetFilterCnts` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#FilterCounters[`github.com/shirou/gopsutil/v4/net.FilterCounters`^].

[id="fn_ps_net_iocnts"]
===== `psNetIoCnts`
[source,go]
.Function Signature
----
func psNetIoCnts(perNIC bool) (ioCnts [](github.com/shirou/gopsutil/v4/net).IOCountersStat, err error)
----

`psNetIoCnts` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#IOCounters[`github.com/shirou/gopsutil/v4/net.IOCounters`^].

[id="fn_ps_net_iocntsfl"]
===== `psNetIoCntsFile`
[source,go]
.Function Signature
----
func psNetIoCntsFile(perNIC bool, filepath string) (ioCnts [](github.com/shirou/gopsutil/v4/net).IOCountersStat, err error)
----

`psNetIoCntsFile` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#IOCountersByFile[`github.com/shirou/gopsutil/v4/net.IOCountersByFile`^].

[id="fn_ps_net_ifaces"]
===== `psNetIfaces`
[source,go]
.Function Signature
----
func psNetIfaces() (ioCnts [](github.com/shirou/gopsutil/v4/net).InterfaceStatList, err error)
----

`psNetIfaces` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#Interfaces[`github.com/shirou/gopsutil/v4/net.Interfaces`^].

[id="fn_ps_net_pids"]
===== `psNetPids`
[source,go]
.Function Signature
----
func psNetPids() (pids []int32, err error)
----

`psNetPids` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#Pids[`github.com/shirou/gopsutil/v4/net.Pids`^].

[id="fn_ps_net_protocnts"]
===== `psNetProtoCnt`
[source,go]
.Function Signature
----
func psNetProtoCnt(protos []string) (protoCnts [](github.com/shirou/gopsutil/v4/net).ProtoCountersStat, err error)
----

[WARNING]
====
This only works properly on Linux currently per upstream.
====

`psNetProtoCnt` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net#ProtoCounters[`github.com/shirou/gopsutil/v4/net.ProtoCounters`^].

[id="fn_ps_net_rev"]
===== `psNetRev`
[source,go]
.Function Signature
----
func psNetRev(b []byte) (out []byte)
----

[WARNING]
====
This function only exists on Linux.
====

`psNetRev` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/net?GOOS=linux#Reverse[`github.com/shirou/gopsutil/v4/net.Reverse`^].

[id="fn_ps_proc"]
==== Processes

[id="fn_ps_procs_procs"]
===== `psProcs`
[source,go]
.Function Signature
----
func psProcs(pid int32) (procs []*(github.com/shirou/gopsutil/v4/process).Process, err error)
----

`psProcs` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/process#Processes[`github.com/shirou/gopsutil/v4/proc.Processes`^].

[id="fn_ps_proc_new"]
===== `psProcNew`
[source,go]
.Function Signature
----
func psProcNew(pid int32) (proc *(github.com/shirou/gopsutil/v4/process).Process, err error)
----

`psProcNew` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/process#NewProcess[`github.com/shirou/gopsutil/v4/proc.NewProcess`^].

[id="fn_ps_proc_pids"]
===== `psProcPids`
[source,go]
.Function Signature
----
func psProcPids() (pids []int32, err error)
----

`psProcPids` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/process#Pids[`github.com/shirou/gopsutil/v4/proc.Pids`^].

[id="fn_ps_proc_pidxst"]
===== `psProcPidExists`
[source,go]
.Function Signature
----
func psProcPidExists(pid int32) (exists bool, err error)
----

`psProcPidExists` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/process#PidExists[`github.com/shirou/gopsutil/v4/proc.PidExists`^].

[id="fn_ps_sns"]
==== Sensors/Thermals

[id="fn_ps_sns_extemp"]
===== `psSensorExTemp`
[source,go]
.Function Signature
----
func psSensorExTemp() (temps [](github.com/shirou/gopsutil/v4/sensors).ExTemperature, err error)
----

[WARNING]
====
This function only exists on Linux.
====

`psSensorExTemp` wraps link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/sensors?GOOS=linux#NewExLinux[`github.com/shirou/gopsutil/v4/sensors.NewExLinux`^].link:https://pkg.go.dev/github.com/shirou/gopsutil/v4/sensors?GOOS=linux#TemperatureWithContext[`github.com/shirou/gopsutil/v4/sensors.TemperatureWithContext`^].

[id="fn_ps_sns_temps"]
===== `psSensorTemps`
[source,go]
.Function Signature
----
func psSensorTemps() (temps [](github.com/shirou/gopsutil/v4/sensors).TemperatureStat, err error)
----

`psSensorTemps` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/sensors#SensorsTemperatures[`github.com/shirou/gopsutil/v4/sensors.SensorsTemperatures`^].

[id="fn_ps_winsvc"]
==== Windows Services

[WARNING]
====
All of these functions are only available on Windows.
====

[id="fn_ps_winsvc_list"]
===== `psWinsvcList`
[source,go]
.Function Signature
----
func psWinsvcList() (svcs [](github.com/shirou/gopsutil/v4/winservices).Service, err error)
----

[WARNING]
====
This function is only available on Windows.
====

`psWinsvcList` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/winservices?GOOS=windows#ListServices[`github.com/shirou/gopsutil/v4/winservices.ListServices`^].

[id="fn_ps_winsvc_new"]
===== `psWinsvcNew`
[source,go]
.Function Signature
----
func psWinsvcNew(svcName string) (svc *(github.com/shirou/gopsutil/v4/winservices).Service, err error)
----

[WARNING]
====
This function is only available on Windows.
====

`psWinsvcNew` directly calls https://pkg.go.dev/github.com/shirou/gopsutil/v4/winservices?GOOS=windows#NewService[`github.com/shirou/gopsutil/v4/winservices.NewService`^].

[id="fn_str"]
=== Strings

[id="fn_str_extindent"]
==== `extIndent`
[source,go]
.Function Signature
----
func extIndent(
		levels int,
		skipFirst, skipEmpty, skipWhitespace bool,
		indentString, input string,
	) (out string)
----

`extIndent` allows for a MUCH more flexible indenter than the `sprig` `indent` function.

It works with both Windows (`\r\n`) and POSIX (`\n`) linebreaks.

[TIP]
====
If `<indentString>` is set to `\n` and `<levels>` is always set to `1`, this function can even be used to doubelspace text!
====

It has quite a few arguments, however:

[source,gotemplate]
----
{{ extIndent <levels> <skipFirst> <skipEmpty> <skipWhitespace> <indentString> <input> }}
----

Where:

* `<levels>`: The level of indentation for the text. If less than or equal to `0`, `extIndent` just returns `<input>` as-is and NO-OPs otherwise.
* `<skipFirst>`: If true, skip indenting the first line. This is particularly handy if you like to visually align your function calls in your templates.
* `<skipEmpty>`: If true, do not add an indent to *empty* lines (where an "empty line" means "only has a linebreak").
* `<skipWhitespace>`: If true, do not add an indent to lines that *only* consist of whitespace (spaces, tabs, etc.) and a linebreak.
* `<indentString>`: The string to use as the "indent character". This can be any string, such as `" "`, `"\t"`, `"."`, `"|"`, `"=="` etc.
* `<input>`: The text to be indented. Because it is the last argument, `extIndent` works with pipelined text as well.

[id="fn_sys"]
=== System/Platform/Architecture

[id="fn_sys_arch"]
==== `sysArch`
[source,go]
.Function Signature
----
func sysArch() (out string)
----

Returns the https://pkg.go.dev/runtime#GOARCH[`runtime.GOARCH`^] constant.

[id="fn_sys_numcpu"]
==== `sysNumCpu`
[source,go]
.Function Signature
----
func sysNumCpu() (cnt int)
----

`sysNumCpu` directly calls https://pkg.go.dev/runtime#NumCPU[`runtime.NumCPU`^].

[id="fn_sys_os"]
==== `sysOsName`
[source,go]
.Function Signature
----
func sysOsNm() (out string)
----

Returns the https://pkg.go.dev/runtime#GOOS[`runtime.GOOS`^] constant.

[id="fn_sys_rntm"]
==== `sysRuntime`
[source,go]
.Function Signature
----
func sysRuntime() (out map[string]string)
----

This function returns a `map[string]string` of various information from the https://pkg.go.dev/runtime[`runtime` stdlib library^].

Specifically, the following are returned.

[TIP]
====
The value type is a direct link to the `runtime` documentation providing more detail about the associated value.

Because all values are mapped as strings, they can be converted back to their native type via e.g. the https://masterminds.github.io/sprig/conversion.html[Sprig conversion functions^] if necessary.
====

.`sysRuntime` Values
[cols="^.^3m,^.^3",options="header"]
|===
| Key | Value Type

| compiler | https://pkg.go.dev/runtime#Compiler[string^]
| arch | https://pkg.go.dev/runtime#GOARCH[string^]
| os | https://pkg.go.dev/runtime#GOOS[string^]
| maxprocs | https://pkg.go.dev/runtime#GOMAXPROCS[int^] footnote:[For safety concerns, `sprigx` does not allow *setting* `GOMAXPROCS`, this value only contains the *current* `GOMAXPROCS` value.]
| cpu_cnt | https://pkg.go.dev/runtime#NumCPU[int^]
| num_cgo | https://pkg.go.dev/runtime#NumCgoCall[int^]
| num_go | https://pkg.go.dev/runtime#NumGoroutine[int^]
| go_ver | https://pkg.go.dev/runtime#Version[string^]
|===

As a convenience, some of these values also have their own dedicated functions as well:

* <<fn_sys_arch>>
* <<fn_sys_numcpu>>
* <<fn_sys_os>>
