types

package module
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2025 License: MIT Imports: 2 Imported by: 12

README

Go Types

Provides Option, Result and Tuple generic types for go and supporting methods

Tuple

Tuples represent 2 or more values as a single type. They are very helpful when creating generic go code that utilizes function signatures.

For full examples of the tuple types see the tuple tests

Tuple2[T1, T2 any]

Represents 2 values in a single type.

The easiest way to create a Tuple2[T1, T2 any] is to use the tuple package.

package main

import(
    "fmt"
    "github.com/patrickhuber/go-types/tuple"
)
func main(){
    t := tuple.New2(1, "hello world")
    i, s := t.Deconstruct()
    fmt.Printf("%d %s", i, s)
}

Go Playground

TupleN[T1 .. TN]

There are a total of 16 Tuple types in this library. They each have similar methods and support functions generated by code gen.

Option[T any]

Option[T any] represents Something or Nothing.

The core types for the option type are in the types package. Helper functions for creating options are located in the option package.

For full examples of the option types see the option tests

Some[T any]

Some[T any] is an Option[T any] with a value.

The easiest way to create a Some type is to use the option package

package main

import (
    "github.com/patrickhuber/go-types/option"
    "github.com/patrickhuber/go-types"
    "fmt"
)
func main(){
    // creates a types.Some[int] using type inference
    op := option.Some(1) 
    // because Some and None are types, a types switch can be used
    switch o := op.(type){
    case types.Some[int]:
        fmt.Println("some", o.Value)
    case types.None[int]:
        fmt.Println("none")
    }    
}

Go Playground

None[T any]

None[T any] is an Option[T any] with no value

The easiest way to create a None type is to use the option package

package main

import (
    "github.com/patrickhuber/go-types/option"
    "github.com/patrickhuber/go-types"
    "fmt"
)
func main(){
    // creates a types.None[int]
    op := option.None[int]() 
    // because Some and None are types, a types switch can be used
    switch o := op.(type){
    case types.Some[int]:
        fmt.Println("some", o.Value)
    case types.None[int]:
        fmt.Println("none")
    }    
}

Go Playground

Result[T any]

Result[T any] represents a value Ok or an Error

The core types for result type are in the types package. Helper functions for creating results are located in the result package.

For full examples of the result types see the result tests

Ok[T any]

Ok[T any] is a successful Result[T any] with a value

The easiest way to create a Ok type is to use the result package

package main

import (
    "github.com/patrickhuber/go-types/result"
    "github.com/patrickhuber/go-types"
    "fmt"
)
func main(){
    // creates a Ok[int] result using type inference
    res := result.Ok(1) 
    // because Ok and Error are types, a types switch can be used
    switch r := res.(type){
    case types.Ok[int]:
        fmt.Println("value", r.Value)
    case types.Error[int]:
        fmt.Println(r.Value)
    }    
}

Go Playground

Error[T any]

Error[T any] is a failed Result[T any] with an error

The easiest way to create an Error type is to use the result package

package main

import (
    "github.com/patrickhuber/go-types/result"
    "github.com/patrickhuber/go-types"
    "fmt"
)
func main(){
    // creates a Error[int] result
    res := result.Errorf[int]("error")

    // because Ok and Error are types, a types switch can be used
    switch r := res.(type){
    case types.Ok[int]:
        fmt.Println("value", r.Value)
    case types.Error[int]:
        fmt.Println(r.Value)
    }    
}

Go Playground

Error Handling

Result[T any] can be used to avoid if err != nil repetition in code by passing in Result[T any] objects instead of values.

Result[T any] must be called with defer or panics will not be recovered.

func MyFunction() (res Result[any]){
    defer handle.Error(&res)
    assert.IsNotNil(nil)
}

System panics will not be trapped by handle.Error because they will not wrap ErrRecoverable. Any function in this module that panics, will panic with ErrRecoverable.

Use the method Throw if you want to panic with a recoverable error as it performs the error wrapping of ErrRecoverable using fmt.Errorf.

Baseline

As a baseline look at the idiomatic go example

Assert

To utilize the library without modifying dependencies you can use defer handle.Error(&res) along with the assert package to remove if err != nil { return err} checks. The assert example shows how.

Wrapping

To use results in return types by wrapping existing functions that return errors see the wrap example

The result.Unwrap() syntax is very similar to rust's ? syntax and dramatically reduces the verticle size of the code while still handing errors.

Documentation

Overview

this file was generated by "go generate" please do not edit

Index

Constants

This section is empty.

Variables

View Source
var ErrRecoverable = errors.New("")

ErrRecoverable is a marker used when handling panics. If the error is wrapped in ErrRecoverable defer handle.Error will recover The most common way to use ErrRecoverable is to call the Throw method

Functions

func Throw added in v0.4.0

func Throw(err error)

Throw wraps the given error with ErrRecoverable and then panics

Types

type Error

type Error[T any] struct {
	Value error
}

func (Error[T]) Deconstruct added in v0.5.0

func (e Error[T]) Deconstruct() (T, error)

Deconstruct implements Result[T].Deconstruct()

func (Error[T]) IsError added in v0.5.0

func (e Error[T]) IsError(err ...error) bool

IsError implements Result[T].IsError()

func (Error[T]) IsOk added in v0.5.0

func (e Error[T]) IsOk() bool

IsOk implements Result[T].IsOk()

func (Error[T]) MapError added in v0.6.1

func (o Error[T]) MapError(errMap func(error) error) Result[T]

MapError implements Result[T].MapError

func (Error[T]) Unwrap added in v0.5.0

func (e Error[T]) Unwrap() T

Unwrap() implements Result[T].Unwrap()

type None

type None[T any] struct {
}

None represents an Option[T] that does not have a value

func (None[T]) Deconstruct added in v0.5.0

func (None[T]) Deconstruct() (T, bool)

Deconstruct implements Option[T].Deconstruct

func (None[T]) IsNone added in v0.5.0

func (n None[T]) IsNone() bool

IsNone implements Option[T].IsNone

func (None[T]) IsSome added in v0.5.0

func (n None[T]) IsSome() bool

IsSome implements Option[T].IsSome

func (None[T]) Unwrap added in v0.5.0

func (n None[T]) Unwrap() T

Unwrap implements Option[T].Unwrap

func (None[T]) UnwrapOr added in v0.5.1

func (n None[T]) UnwrapOr(other T) T

UnwrapOr implements Option[T].UnwrapOr

type Ok

type Ok[T any] struct {
	Value T
}

func (Ok[T]) Deconstruct added in v0.5.0

func (o Ok[T]) Deconstruct() (T, error)

Deconstruct implements Result[T].Deconstruct

func (Ok[T]) IsError added in v0.5.0

func (o Ok[T]) IsError(err ...error) bool

IsError implements Result[T].IsError

func (Ok[T]) IsOk added in v0.5.0

func (o Ok[T]) IsOk() bool

IsOk implements Result[T].IsOk

func (Ok[T]) MapError added in v0.6.1

func (o Ok[T]) MapError(errMap func(error) error) Result[T]

MapError implements Result[T].MapError

func (Ok[T]) Unwrap added in v0.5.0

func (o Ok[T]) Unwrap() T

Unwrap implements Result[T].Unwrap

type Option

type Option[T any] interface {

	// Deconstruct returns Value, true if the underlying type is Some
	// returns Default, false if the underlying type is None
	Deconstruct() (T, bool)

	// IsSome returns true if the underlying type is Some
	IsSome() bool

	// IsNone returns true if the underlying type is None
	IsNone() bool

	// Unwrap unwraps the Option to its underlying value if the type is Some and
	// panics if the type is None
	Unwrap() T

	// UnwrapOr unwraps the Option if it is Some[T]. If the option is None[T] the supplied value 'other' is returned.
	UnwrapOr(other T) T
	// contains filtered or unexported methods
}

Option represents a value or nothing

func NewNone

func NewNone[T any]() Option[T]

NewNone returns a None[T]

func NewSome

func NewSome[T any](value T) Option[T]

NewSome returns a Some[T] that wraps the value T

type Result

type Result[T any] interface {

	// Deconstruct expands the result to its underlying values.
	// if the result is an error, T contains the zero value.
	Deconstruct() (T, error)

	// IsOk returns true for Ok results, false for Error results.
	IsOk() bool

	// IsError returns false for Ok results, true for Error results.
	// IsError returns true for Error results where the err list is empty
	// IsError returns false for Error results where the err list has at least one error that doesn't match errors.Is
	// IsError returns true for Error results where the err list has at least one error that matches errors.Is
	IsError(err ...error) bool

	// Unwrap attempts to unwrap the result to its value.
	// If the result is an Error, Unwrap will panic.
	// Unwrap is meant to be used with handle.Error(*types.Result)
	Unwrap() T

	// MapError maps the error to another error
	// The function is meant to reduce if err != nil boilerplate and allows the user to add additioanl error information
	MapError(func(error) error) Result[T]
	// contains filtered or unexported methods
}

Result represents a value or an error

func Cast

func Cast[TSource, TTarget any](source TSource) Result[TTarget]

Cast pereforms a type assertion on the given source to the target type. If the type assertion succeeds, Ok[Target] is returned. If the type assertion fails, Error[TTarget] is returned.

func Castf

func Castf[TSource, TTarget any](source TSource, format string, args ...any) Result[TTarget]

Castf pereforms a type assertion on the given source to the target type. If the type assertion succeeds, Ok[Target] is returned. If the type assertion fails, Error[TTarget] with the giiven message is returned.

func NewError

func NewError[T any](value error) Result[T]

NewError returns an error over the supplied error

func NewOk

func NewOk[T any](value T) Result[T]

NewOk returns an Ok[T] that wraps the given value

func NewResult

func NewResult[T any](ok T, err error) Result[T]

NewResult returns a Error if err is not nil and Ok if err is nil. Most consumers of the module will use the `result` package `Ok` and `Error` functions instead.

type Some

type Some[T any] struct {
	Value T
}

func (Some[T]) Deconstruct added in v0.5.0

func (s Some[T]) Deconstruct() (T, bool)

Deconstruct implements Option[T].Deconstruct()

func (Some[T]) IsNone added in v0.5.0

func (s Some[T]) IsNone() bool

IsNone implementes Option[T].IsNone

func (Some[T]) IsSome added in v0.5.0

func (s Some[T]) IsSome() bool

IsSome implementes Option[T].IsSome

func (Some[T]) Unwrap added in v0.5.0

func (s Some[T]) Unwrap() T

Unwrap impelements Option[T].Unwrap

func (Some[T]) UnwrapOr added in v0.5.1

func (s Some[T]) UnwrapOr(other T) T

UnwrapOr implements Option[T].UnwrapOr

type Tuple10 added in v0.4.3

type Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
}

func NewTuple10 added in v0.4.3

func NewTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10) Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]

func (Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Deconstruct added in v0.4.3

func (t Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)

type Tuple11 added in v0.4.3

type Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
}

func NewTuple11 added in v0.4.3

func NewTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11) Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]

func (Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Deconstruct added in v0.4.3

func (t Tuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)

type Tuple12 added in v0.4.3

type Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
	Value12 T12
}

func NewTuple12 added in v0.4.3

func NewTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12) Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]

func (Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Deconstruct added in v0.4.3

func (t Tuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)

type Tuple13 added in v0.4.3

type Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
	Value12 T12
	Value13 T13
}

func NewTuple13 added in v0.4.3

func NewTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13) Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]

func (Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Deconstruct added in v0.4.3

func (t Tuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)

type Tuple14 added in v0.4.3

type Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
	Value12 T12
	Value13 T13
	Value14 T14
}

func NewTuple14 added in v0.4.3

func NewTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14) Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]

func (Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Deconstruct added in v0.4.3

func (t Tuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)

type Tuple15 added in v0.4.3

type Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
	Value12 T12
	Value13 T13
	Value14 T14
	Value15 T15
}

func NewTuple15 added in v0.4.3

func NewTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15) Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]

func (Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) Deconstruct added in v0.4.3

func (t Tuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)

type Tuple16 added in v0.4.3

type Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any] struct {
	Value1  T1
	Value2  T2
	Value3  T3
	Value4  T4
	Value5  T5
	Value6  T6
	Value7  T7
	Value8  T8
	Value9  T9
	Value10 T10
	Value11 T11
	Value12 T12
	Value13 T13
	Value14 T14
	Value15 T15
	Value16 T16
}

func NewTuple16 added in v0.4.3

func NewTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9, t10 T10, t11 T11, t12 T12, t13 T13, t14 T14, t15 T15, t16 T16) Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]

func (Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) Deconstruct added in v0.4.3

func (t Tuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)

type Tuple2

type Tuple2[T1, T2 any] struct {
	Value1 T1
	Value2 T2
}

func NewTuple2

func NewTuple2[T1, T2 any](t1 T1, t2 T2) Tuple2[T1, T2]

func (Tuple2[T1, T2]) Deconstruct

func (t Tuple2[T1, T2]) Deconstruct() (T1, T2)

type Tuple3

type Tuple3[T1, T2, T3 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
}

func NewTuple3

func NewTuple3[T1, T2, T3 any](t1 T1, t2 T2, t3 T3) Tuple3[T1, T2, T3]

func (Tuple3[T1, T2, T3]) Deconstruct

func (t Tuple3[T1, T2, T3]) Deconstruct() (T1, T2, T3)

type Tuple4

type Tuple4[T1, T2, T3, T4 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
}

func NewTuple4

func NewTuple4[T1, T2, T3, T4 any](t1 T1, t2 T2, t3 T3, t4 T4) Tuple4[T1, T2, T3, T4]

func (Tuple4[T1, T2, T3, T4]) Deconstruct

func (t Tuple4[T1, T2, T3, T4]) Deconstruct() (T1, T2, T3, T4)

type Tuple5 added in v0.4.3

type Tuple5[T1, T2, T3, T4, T5 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
	Value5 T5
}

func NewTuple5 added in v0.4.3

func NewTuple5[T1, T2, T3, T4, T5 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5) Tuple5[T1, T2, T3, T4, T5]

func (Tuple5[T1, T2, T3, T4, T5]) Deconstruct added in v0.4.3

func (t Tuple5[T1, T2, T3, T4, T5]) Deconstruct() (T1, T2, T3, T4, T5)

type Tuple6 added in v0.4.3

type Tuple6[T1, T2, T3, T4, T5, T6 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
	Value5 T5
	Value6 T6
}

func NewTuple6 added in v0.4.3

func NewTuple6[T1, T2, T3, T4, T5, T6 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) Tuple6[T1, T2, T3, T4, T5, T6]

func (Tuple6[T1, T2, T3, T4, T5, T6]) Deconstruct added in v0.4.3

func (t Tuple6[T1, T2, T3, T4, T5, T6]) Deconstruct() (T1, T2, T3, T4, T5, T6)

type Tuple7 added in v0.4.3

type Tuple7[T1, T2, T3, T4, T5, T6, T7 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
	Value5 T5
	Value6 T6
	Value7 T7
}

func NewTuple7 added in v0.4.3

func NewTuple7[T1, T2, T3, T4, T5, T6, T7 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7) Tuple7[T1, T2, T3, T4, T5, T6, T7]

func (Tuple7[T1, T2, T3, T4, T5, T6, T7]) Deconstruct added in v0.4.3

func (t Tuple7[T1, T2, T3, T4, T5, T6, T7]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7)

type Tuple8 added in v0.4.3

type Tuple8[T1, T2, T3, T4, T5, T6, T7, T8 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
	Value5 T5
	Value6 T6
	Value7 T7
	Value8 T8
}

func NewTuple8 added in v0.4.3

func NewTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8) Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]

func (Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Deconstruct added in v0.4.3

func (t Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8)

type Tuple9 added in v0.4.3

type Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any] struct {
	Value1 T1
	Value2 T2
	Value3 T3
	Value4 T4
	Value5 T5
	Value6 T6
	Value7 T7
	Value8 T8
	Value9 T9
}

func NewTuple9 added in v0.4.3

func NewTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t1 T1, t2 T2, t3 T3, t4 T4, t5 T5, t6 T6, t7 T7, t8 T8, t9 T9) Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]

func (Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Deconstruct added in v0.4.3

func (t Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) Deconstruct() (T1, T2, T3, T4, T5, T6, T7, T8, T9)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL