ruler

package
v0.4.18 Latest Latest
Warning

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

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Rulers are horizontal separators which may be used in `psv` tables to help visually separate rows within a table.

Rulers grow and shrink depending on the width of the data in each column.

Usage

Creation from a template

r, err := ruler.New(
                   ruler.WithTemplate(template), // optional
                   )

Ruler Template structure

The template structure was chosen with with the goals:

  • allow it to be shortened without loss of detail.

  • templates should be in the same order that they would appear in an actual ruler.

  • all character duplication should be removed.

    Template `| -+` : ^^^^ : |||| outer border --+||| padding ---+|| horizontal line ----+| internal column separator -----+

Example Templates

Template    Example Ruler                   Notes
`|-`        `|-----|----------|-------|`    single horizontal line, no padding
`|=`        `|=====|==========|=======|`    double horizontal line, no padding
`| -`       `| --- | -------- | ----- |`    single horizontal line, with padding (default)
`| -+`      `| --- + -------- + ----- |`    use `+` to indicate "crossing lines" internally
`+ -`       `+ --- + -------- + ----- +`    use `+` to indicate "crossing lines" on all intersections
`+-`        `+-----+----------+-------+`    enclosing border, `+` for intersections and no padding

Index

Examples

Constants

View Source
const (
	TAB   byte = '\x09'
	SPACE byte = '\x20'
)

Variables

View Source
var (
	ErrEmptyLine    = errors.New("empty line")
	ErrNotARuler    = errors.New("not a ruler")
	ErrInvalidFlags = errors.New("invalid flags")
	ErrUnknownToken = errors.New("unknown token") // should never happen
)
View Source
var (
	ErrLongTemplate      = errors.New("ruler template is too long")
	ErrMissingBorder     = errors.New("no ruler characters found")
	ErrMissingHorizontal = errors.New("no horizontal ruler characters found (`-` or `=`)")
	ErrNonRulerChar      = errors.New("not a ruler character")
	ErrBadVertical       = errors.New("not a vertical ruler character")
	ErrBadHorizontal     = errors.New("not a horizontal ruler character")
)
View Source
var DefaultConfig = &Config{
	Vertical:       []byte("|+"),
	Horizontal:     []byte("-="),
	Alignment:      []byte(":"),
	Point:          []byte(".,"),
	SortAscending:  []byte("^"),
	SortDescending: []byte("v"),
	SortPriority:   []byte("1234567"),
}

Functions

func IsAlignment

func IsAlignment(char byte) bool

func IsHorizontal

func IsHorizontal(char byte) bool

func IsPadding

func IsPadding(char byte) bool

func IsPoint

func IsPoint(char byte) bool

func IsSortAscending

func IsSortAscending(char byte) bool

func IsSortDescending

func IsSortDescending(char byte) bool

func IsSortPriority

func IsSortPriority(char byte) bool

func IsVertical

func IsVertical(char byte) bool

Types

type Config

type Config struct {
	Vertical       []byte
	Horizontal     []byte
	Alignment      []byte
	Point          []byte
	SortAscending  []byte
	SortDescending []byte
	SortPriority   []byte
}

func (*Config) IsAlignment

func (cfg *Config) IsAlignment(char byte) bool

func (*Config) IsHorizontal

func (cfg *Config) IsHorizontal(char byte) bool

func (*Config) IsPadding

func (cfg *Config) IsPadding(char byte) bool

func (*Config) IsPoint

func (cfg *Config) IsPoint(char byte) bool

func (*Config) IsSortAscending

func (cfg *Config) IsSortAscending(char byte) bool

func (*Config) IsSortDescending

func (cfg *Config) IsSortDescending(char byte) bool

func (*Config) IsSortPriority

func (cfg *Config) IsSortPriority(char byte) bool

func (*Config) IsVertical

func (cfg *Config) IsVertical(char byte) bool

type JSONMap

type JSONMap struct {
	Template string `json:"t,omitempty"`
}

mapping object for JSONMap encoding: map template to/from a string

type Ruler

type Ruler struct {
	Border     byte // outer vertical line: `|` or `+`
	Padding    byte // horizontal padding:  `-`, `=` or ` `
	Horizontal byte // horizontal line:     `-` or `=`
	Separator  byte // inner vertical line: `|` or `+`
}

Ruler represents a horizontal separator line within a table.

Notes:

  • this Ruler struct DOES NOT maintain information about the width, alignment or sorting hints. See column.Info
  • currently, rulers are intentionally limited to 8-bit characters
  • only ASCII characters are used for line drawing
  • tabs will be converted to spaces
  • non-breaking space (0xa0) may be used
Example (Zero_value_invalid)
package main

import (
	"fmt"

	"codeberg.org/japh/psv/internal/ruler"
)

func main() {
	// a zero-value ruler is not usable if not validated
	r := ruler.Ruler{}

	fmt.Printf("r := ruler.Ruler{} // without validation\n")
	fmt.Printf("Template:          %q\n", r.Template())
	fmt.Printf("Border Char:       %q\n", r.Border)
	fmt.Printf("Padding Char:      %q\n", r.Padding)
	fmt.Printf("Horizontal Char:   %q\n", r.Horizontal)
	fmt.Printf("Separator Char:    %q\n", r.Separator)

}
Output:

r := ruler.Ruler{} // without validation
Template:          "\x00\x00\x00\x00"
Border Char:       '\x00'
Padding Char:      '\x00'
Horizontal Char:   '\x00'
Separator Char:    '\x00'

func FromTemplate

func FromTemplate(template string) Ruler

func New

func New() Ruler

New creates a new ruler from a template string. See Ruler for template construction rules.

An error will be returned if the template is too long or if it contains invalid characters.

Example (Bad_templates)
package main

import (
	"fmt"

	"codeberg.org/japh/psv/internal/ruler"
)

func main() {
	// templates containing invalid characters
	badTemplates := []string{
		`x`,     // `x` does not represent a vertical or horizontal line
		`-`,     // `-` may only be used as a horizontal line - wrong position
		`+x`,    //
		`++`,    // `+` may only be used as a vertical character
		``,      // invalid when explicitly set via WithTemplate
		`|`,     // invalid when explicitly set via WithTemplate
		`+`,     // invalid when explicitly set via WithTemplate
		`+ x`,   //
		`+ |`,   // vertical line instead of a horizontal line
		`| =x`,  //
		`| ==`,  // horizontal line instead of a vertical line -
		`| -|x`, // too long
	}

	for _, t := range badTemplates {
		r := ruler.New()
		err := r.ApplyTemplate(t)
		fmt.Printf("r, err := New(%-7q) => error: %q\n", t, err)
	}

}
Output:

r, err := New("x"    ) => error: "'x' is not a ruler character"
r, err := New("-"    ) => error: "unexpected '-' is not a vertical ruler character for the outer border"
r, err := New("+x"   ) => error: "'x' is not a ruler character"
r, err := New("++"   ) => error: "no horizontal ruler characters found (`-` or `=`)"
r, err := New(""     ) => error: "no ruler characters found"
r, err := New("|"    ) => error: "no horizontal ruler characters found (`-` or `=`)"
r, err := New("+"    ) => error: "no horizontal ruler characters found (`-` or `=`)"
r, err := New("+ x"  ) => error: "'x' is not a ruler character"
r, err := New("+ |"  ) => error: "unexpected '|' is not a horizontal ruler character"
r, err := New("| =x" ) => error: "'x' is not a ruler character"
r, err := New("| ==" ) => error: "unexpected '=' is not a vertical ruler character for column separators"
r, err := New("| -|x") => error: "ruler template is too long \"| -|x\" (max 4 characters)"
Example (Default)
package main

import (
	"fmt"

	"codeberg.org/japh/psv/internal/ruler"
)

func main() {
	r := ruler.New()
	err := r.Validate()

	fmt.Printf("r := ruler.New() // preferred default ruler\n")
	fmt.Printf("Default Validation Error: %v\n", err)
	fmt.Printf("Default Template:         %q\n", r.Template())
	fmt.Printf("Default Border Char:      %q\n", r.Border)
	fmt.Printf("Default Padding Char:     %q\n", r.Padding)
	fmt.Printf("Default Horizontal Char:  %q\n", r.Horizontal)
	fmt.Printf("Default Separator Char:   %q\n", r.Separator)

}
Output:

r := ruler.New() // preferred default ruler
Default Validation Error: <nil>
Default Template:         "| -|"
Default Border Char:      '|'
Default Padding Char:     ' '
Default Horizontal Char:  '-'
Default Separator Char:   '|'
Example (Good_templates)
package main

import (
	"fmt"

	"codeberg.org/japh/psv/internal/ruler"
)

func main() {
	goodTemplates := []struct {
		template string
		notes    string
	}{
		{"+-", "unpadded"},
		{"|=", "unpadded, emphasised"},
		{"| =", "emphasised"},
		{"+ -|", "custom"},
	}

	for _, t := range goodTemplates {
		r := ruler.New()
		err := r.ApplyTemplate(t.template)
		if err != nil {
			fmt.Printf("r, err := New(%-6q) => unexpected error: %q\n", t.template, err)
			continue
		}
		fmt.Printf("r, err := New(%-6q) => %q %s\n", t.template, r.Template(), t.notes)
	}

}
Output:

r, err := New("+-"  ) => "+--+" unpadded
r, err := New("|="  ) => "|==|" unpadded, emphasised
r, err := New("| =" ) => "| =|" emphasised
r, err := New("+ -|") => "+ -|" custom

func Parse

func Parse(in string) (rlr Ruler, flgs []column.Flags, err error)

Parse parses a ruler from a line of text, e.g. from an existing table.

Challenges:

  • rulers from text will often be inaccurately formed e.g. `| +.|=--:-|^` is a very compact ruler, but in its correct form (assuming 5 chars per column) it would be `| ===== + ====. + ==:== + ====^ |`

i.e.:

  • the `+` is used to _separate_ all columns
  • the `.` will be aligned depending on the number of decimal places appear in the data
  • the first horizontal character is '=', so it is used for the whole line
  • etc.

Rulers have two effects: - define the appearance of a ruler (vertical and horizontal lines and padding) - configure the alignment and sorting preferences for each column

If the slice of column flags returned is longer than the number of columns in the table, then all additional flags should be ignored.

Also, column flags should only be applied to the table if the table's columns are not already configured.

func (*Ruler) ApplyTemplate

func (r *Ruler) ApplyTemplate(template string) error

ApplyTemplate specifies all of a ruler's characteristics via a single string template.

The template must be <= 4 characters long.

  • character 0: the vertical line ('|' or '+') used at the start and end of a ruler
  • character 1: the padding charactar to use, or a horizontal line ('-' or '=')
  • character 2: the horizontal line to use ('-' or '=')
  • character 3: the vertical line ('|' or '+') to use between columns within the ruler

The template will be normalised if it is too short. i.e., if missing...

  • the border and padding characters will be copied from the default template ('|')
  • the horizontal character will be copied from the padding character, if the padding character is a horizontal line
  • otherwise, the horizontal character will be copied from the default template ('-')
  • the column separator character will be copied from the border

An error will be returned if:

  • the template has more than 4 characters
  • the template contains characters that do not match the rules laid out above

func (*Ruler) HasPadding

func (r *Ruler) HasPadding() bool

HasPadding returns true if the ruler should be padded

func (*Ruler) HorizontalString

func (r *Ruler) HorizontalString() string

HorizontalString returns a string representing the ruler's horizontal line.

func (*Ruler) InitBorder

func (r *Ruler) InitBorder(char byte)

InitBorder sets the outer border character of a ruler, if one has not been set yet

func (*Ruler) InitHorizontal

func (r *Ruler) InitHorizontal(char byte)

InitHorizontal sets the horizontal line character of a rulerm if one has not been set yet.

Setting a horizontal line character before a padding character has been set will also set the padding character to the same value.

func (*Ruler) InitPadding

func (r *Ruler) InitPadding(char byte)

InitPadding sets the padding character of a ruler, if one has not been set yet

func (*Ruler) InitSeparator

func (r *Ruler) InitSeparator(char byte)

InitSeparator sets the column separator character of a ruler, if one has not been set yet

func (Ruler) MarshalJSON

func (r Ruler) MarshalJSON() ([]byte, error)

MarshalJSON encodes the ruler to JSON.

Optimisations:

  • only uses short keys: `{"t":…}`

Used mostly for encoding rulers as JSON for tests.

func (*Ruler) PadString

func (r *Ruler) PadString() string

PadString returns a string representing the ruler's padding. If the ruler is not padded, an empty string is returned.

func (*Ruler) String

func (r *Ruler) String() string

String returns a quoted string representation of the ruler, consisting of its position in the table and its template.

This is only intended for debugging. To render a ruler in a table, see [render.Ruler]

func (*Ruler) Template

func (r *Ruler) Template() string

Template returns the ruler's template as a 4-character string

func (*Ruler) UnmarshalJSON

func (r *Ruler) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a json string into a ruler.

Used mostly for decoding rulers from JSON for tests.

func (*Ruler) Validate

func (r *Ruler) Validate() error

Validate a ruler's template

  • missing characters are normalised, using the partial template, and the default as a fallback
  • vertical characters must match `|` or `+`
  • Horizontal characters must match '-', `=` or ` `

The template is normalised in reverse order

  • always keep specified values

Jump to

Keyboard shortcuts

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