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 ¶
- Constants
- Variables
- func IsAlignment(char byte) bool
- func IsHorizontal(char byte) bool
- func IsPadding(char byte) bool
- func IsPoint(char byte) bool
- func IsSortAscending(char byte) bool
- func IsSortDescending(char byte) bool
- func IsSortPriority(char byte) bool
- func IsVertical(char byte) bool
- type Config
- func (cfg *Config) IsAlignment(char byte) bool
- func (cfg *Config) IsHorizontal(char byte) bool
- func (cfg *Config) IsPadding(char byte) bool
- func (cfg *Config) IsPoint(char byte) bool
- func (cfg *Config) IsSortAscending(char byte) bool
- func (cfg *Config) IsSortDescending(char byte) bool
- func (cfg *Config) IsSortPriority(char byte) bool
- func (cfg *Config) IsVertical(char byte) bool
- type JSONMap
- type Ruler
- func (r *Ruler) ApplyTemplate(template string) error
- func (r *Ruler) HasPadding() bool
- func (r *Ruler) HorizontalString() string
- func (r *Ruler) InitBorder(char byte)
- func (r *Ruler) InitHorizontal(char byte)
- func (r *Ruler) InitPadding(char byte)
- func (r *Ruler) InitSeparator(char byte)
- func (r Ruler) MarshalJSON() ([]byte, error)
- func (r *Ruler) PadString() string
- func (r *Ruler) String() string
- func (r *Ruler) Template() string
- func (r *Ruler) UnmarshalJSON(data []byte) error
- func (r *Ruler) Validate() error
Examples ¶
Constants ¶
const ( TAB byte = '\x09' SPACE byte = '\x20' )
Variables ¶
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 )
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") )
Functions ¶
func IsAlignment ¶
func IsHorizontal ¶
func IsSortAscending ¶
func IsSortDescending ¶
func IsSortPriority ¶
func IsVertical ¶
Types ¶
type Config ¶
type Config struct {
Vertical []byte
Horizontal []byte
Alignment []byte
Point []byte
SortAscending []byte
SortDescending []byte
SortPriority []byte
}
func (*Config) IsAlignment ¶
func (*Config) IsHorizontal ¶
func (*Config) IsSortAscending ¶
func (*Config) IsSortDescending ¶
func (*Config) IsSortPriority ¶
func (*Config) IsVertical ¶
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 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 ¶
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 ¶
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 ¶
HasPadding returns true if the ruler should be padded
func (*Ruler) HorizontalString ¶
HorizontalString returns a string representing the ruler's horizontal line.
func (*Ruler) InitBorder ¶
InitBorder sets the outer border character of a ruler, if one has not been set yet
func (*Ruler) InitHorizontal ¶
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 ¶
InitPadding sets the padding character of a ruler, if one has not been set yet
func (*Ruler) InitSeparator ¶
InitSeparator sets the column separator character of a ruler, if one has not been set yet
func (Ruler) MarshalJSON ¶
MarshalJSON encodes the ruler to JSON.
Optimisations:
- only uses short keys: `{"t":…}`
Used mostly for encoding rulers as JSON for tests.
func (*Ruler) PadString ¶
PadString returns a string representing the ruler's padding. If the ruler is not padded, an empty string is returned.
func (*Ruler) 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) UnmarshalJSON ¶
UnmarshalJSON decodes a json string into a ruler.
Used mostly for decoding rulers from JSON for tests.
func (*Ruler) Validate ¶
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