Documentation
¶
Index ¶
- Constants
- Variables
- func LastNumericIndex(field string) int
- func LookupFunc[T any](m *FieldMap) func(fields []T, name string) (value T)
- func VersionIndex(field string) int
- type ColumnFlags
- func (f ColumnFlags) AlignmentString() string
- func (f ColumnFlags) CmpSign() int
- func (f *ColumnFlags) DisableSort()
- func (f *ColumnFlags) EnableSort()
- func (f ColumnFlags) IsAscendingOrder() bool
- func (f ColumnFlags) IsCenterAligned() bool
- func (f ColumnFlags) IsConfigured() bool
- func (f ColumnFlags) IsDefaultAligned() bool
- func (f ColumnFlags) IsDescendingOrder() bool
- func (f ColumnFlags) IsLeftAligned() bool
- func (f ColumnFlags) IsNumberAligned() bool
- func (f ColumnFlags) IsRightAligned() bool
- func (f ColumnFlags) IsSorted() bool
- func (f ColumnFlags) IsVersionAligned() bool
- func (f ColumnFlags) MarshalJSON() ([]byte, error)
- func (f *ColumnFlags) ReverseSort()
- func (f *ColumnFlags) SetAlignment(flags ColumnFlags)
- func (f *ColumnFlags) SetSort(s ColumnFlags)
- func (f *ColumnFlags) SetSortPriority(prio int)
- func (f ColumnFlags) SortPriority() int
- func (f ColumnFlags) SortString() string
- func (f ColumnFlags) String() string
- func (f *ColumnFlags) UnmarshalJSON(data []byte) error
- type ColumnWidth
- type Config
- type Document
- type DocumentItem
- type FieldMap
- type Nullable
- func (n *Nullable[T]) Clear()
- func (n *Nullable[T]) Coalesce(in ...Nullable[T])
- func (n *Nullable[T]) CoalesceValue(values ...T)
- func (n Nullable[T]) Get() (value T, IsSet bool)
- func (n Nullable[T]) IsNull() bool
- func (n Nullable[T]) IsSet() bool
- func (n Nullable[T]) MarshalJSON() ([]byte, error)
- func (n *Nullable[T]) Set(value T)
- func (n *Nullable[T]) UnmarshalJSON(data []byte) error
- func (n Nullable[T]) Value() (value T)
- type Ruler
- type SliceMap
- func (s SliceMap[T]) Cap() int
- func (s *SliceMap[T]) Delete(i int)
- func (s SliceMap[T]) Get(i int) (v T, isInRange bool)
- func (s *SliceMap[T]) Grow(size int)
- func (s SliceMap[T]) Has(i int) bool
- func (s SliceMap[T]) IsEmpty() bool
- func (s *SliceMap[T]) Reset()
- func (s *SliceMap[T]) Set(i int, v ...T)
- func (s SliceMap[T]) Value(i int) (v T)
- type SortColumn
- type Table
- type Text
Examples ¶
Constants ¶
const JSON_NULL = "null"
Variables ¶
Functions ¶
func LastNumericIndex ¶ added in v0.3.2
LastNumericIndex finds the index of the right bound of the last number within a string.
Assumption:
- digits and the decimal point are simple ASCII values with ordinal values < 0x7f
- no need to worry about UTF-8 runes (runes with ordinal values > 0x7f are encoded such that the 1st bit is always set in every byte of the rune's representation)
Process:
- start at the end of the string
- record the index of the last digit encountered
- stop as soon as a '.' is found
Return:
- -1: no numeric value was found (width = total number of runes in the string)
- the index position of the decimal point
- or the index of the last digit + 1
func LookupFunc ¶
LookupFunc returns a function for lazily locating a field in a generic slice.
The returned function, can be used to reliably get a value, without the risk of an out-of-bounds index.
The motivation behind this is to enable a user friendly method of accessing columns of data by name, i.e. over many rows, regardless of whether an individual row is complete or not.
e.g.
// common field map (index of name => field number) m := NewFieldMap( strings.Fields( "one two three" ) ) ints := [][]int{ {1,2,3}, // one field per name {1,2,3,4,5}, // a longer slice {1}, // a shorter slice {}, // an empty slice nil, // a missing slice } lookupInt := LookupFunc[int](m) for _, row := range ints { v1 := lookupInt(row,"one") } structs := [][]*SomeStruct{ ..., // as above, but with structs instead of ints } lookupStruct := LookupFunc[*SomeStruct](m) for _, row := range structs { v3 := lookupStruct(row,"three") }
func VersionIndex ¶ added in v0.3.2
VersionIndex finds the index of the right bound of the first number within a string.
Assumption:
- digits and the decimal point are simple ASCII values with ordinal values < 0x7f
- no need to worry about UTF-8 runes (runes with ordinal values > 0x7f are encoded such that the 1st bit is always set in every byte of the rune's representation)
Process:
- start at the start of the string
- find the first '.' following a digit
Return:
- -1: no numeric value was found (width = total number of runes in the string)
- the index position of the decimal point
- or the index of the last digit + 1
Types ¶
type ColumnFlags ¶
type ColumnFlags byte
ColumnFlags flags are used to specify how a column should be prepared for rendering.
- Alignment - Sorting (incl. sort priority, between 0 and 7)
const ( UnconfiguredFlags ColumnFlags = 0x00 // .... .... - an unconfigured column: default alignment, no sorting UnalignedFlag ColumnFlags = 0x00 // .... .000 - default alignment (left aligned, but without a ruler hint) AlignRightFlag ColumnFlags = 0x01 // .... .001 - align text to the right of the column AlignLeftFlag ColumnFlags = 0x02 // .... .010 - align text to the left of the column AlignCenterFlag ColumnFlags = 0x03 // .... .011 - center text within the column AlignNumericFlag ColumnFlags = 0x04 // .... .100 - align numbers around their decimal point, otherwise right aligned AlignVersionFlag ColumnFlags = 0x07 // .... .111 - align on first point, sort using integer numbers only AlignmentMask ColumnFlags = 0x07 // .... .111 SortColumnFlag ColumnFlags = 0x08 // .... 1... SortAscendingFlag ColumnFlags = 0x00 // ...0 .... SortDescendingFlag ColumnFlags = 0x10 // ...1 .... SortDirectionMask ColumnFlags = 0x10 // ...1 .... SortFlagsMask ColumnFlags = 0x18 // ...1 1... SortPriorityMask ColumnFlags = 0xe0 // 111. .... - 1 .. 7 only )
func (ColumnFlags) AlignmentString ¶
func (f ColumnFlags) AlignmentString() string
func (ColumnFlags) CmpSign ¶
func (f ColumnFlags) CmpSign() int
func (*ColumnFlags) DisableSort ¶
func (f *ColumnFlags) DisableSort()
func (*ColumnFlags) EnableSort ¶
func (f *ColumnFlags) EnableSort()
func (ColumnFlags) IsAscendingOrder ¶
func (f ColumnFlags) IsAscendingOrder() bool
func (ColumnFlags) IsCenterAligned ¶
func (f ColumnFlags) IsCenterAligned() bool
func (ColumnFlags) IsConfigured ¶
func (f ColumnFlags) IsConfigured() bool
func (ColumnFlags) IsDefaultAligned ¶
func (f ColumnFlags) IsDefaultAligned() bool
func (ColumnFlags) IsDescendingOrder ¶
func (f ColumnFlags) IsDescendingOrder() bool
func (ColumnFlags) IsLeftAligned ¶
func (f ColumnFlags) IsLeftAligned() bool
func (ColumnFlags) IsNumberAligned ¶
func (f ColumnFlags) IsNumberAligned() bool
func (ColumnFlags) IsRightAligned ¶
func (f ColumnFlags) IsRightAligned() bool
func (ColumnFlags) IsSorted ¶
func (f ColumnFlags) IsSorted() bool
func (ColumnFlags) IsVersionAligned ¶ added in v0.3.2
func (f ColumnFlags) IsVersionAligned() bool
func (ColumnFlags) MarshalJSON ¶
func (f ColumnFlags) MarshalJSON() ([]byte, error)
MarshalJSON encodes a set of ColumnFlags to JSON.
Used mostly for encoding columns as JSON for tests.
func (*ColumnFlags) ReverseSort ¶
func (f *ColumnFlags) ReverseSort()
func (*ColumnFlags) SetAlignment ¶
func (f *ColumnFlags) SetAlignment(flags ColumnFlags)
func (*ColumnFlags) SetSort ¶
func (f *ColumnFlags) SetSort(s ColumnFlags)
func (*ColumnFlags) SetSortPriority ¶
func (f *ColumnFlags) SetSortPriority(prio int)
SetSortPriority sets the priority used when determining which columns to compare, and in what order, when sorting rows
Setting a column's priority does not enable or disable a column's sort status. (Setting a columns priority should always be compbined with EnableSort())
func (ColumnFlags) SortPriority ¶
func (f ColumnFlags) SortPriority() int
SortPriority returns the sort-priority of the column, if the column is sorted.
Invariants:
- only values between 0 and 7 will be returns
- 0 indicates 'no special priority' or that the column has no sorting preference
func (ColumnFlags) SortString ¶
func (f ColumnFlags) SortString() string
func (ColumnFlags) String ¶
func (f ColumnFlags) String() string
String returns a string representing the alignment and sorting configuration of the column.
The string will have 0, 1, 2 or 3 positional characters for the alignment, sorting direction and sorting priority.
- "" indicates a default column without any alignment or sorting preferences
- alignment character: "l" - left aligned "r" - right aligned "c" - center aligned "n" - numerically aligned
- sorting direction character: "^" - sort column from lowest to highest values "V" - sort column from highest to lowest values
- sorting priority character: [0…7] - comparison priority of the column when sorting
e.g.: "nv2" would indicate "numeric alignment", "sorted in descending order" and "compared with priority 3 while sorting"
func (*ColumnFlags) UnmarshalJSON ¶
func (f *ColumnFlags) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes a json string into a set of ColumnFlags.
Used mostly for decoding columns from JSON for tests.
type ColumnWidth ¶
type ColumnWidth struct { Bytes int // for memory allocation only Chars int // for left, right, center padding Left int // for padding to left of decimal point Right int // for padding to right of decimal point }
Column widths depend on the column's alignment flags
func (*ColumnWidth) ScanField ¶
func (cw *ColumnWidth) ScanField(field string, cf ColumnFlags)
func (ColumnWidth) String ¶
func (cw ColumnWidth) String() string
type Config ¶
type Config struct { LocaleTag Nullable[string] `json:"locale,omitempty"` AlignDocument Nullable[bool] `json:"align,omitempty"` // json encoding for debugging only SquashEmptyColumns Nullable[bool] `json:"squash,omitempty"` Sort Nullable[bool] `json:"sort,omitempty"` Prefix Nullable[string] `json:"prefix,omitempty"` Rulers []*Ruler `json:"rulers,omitempty"` // nil = use rulers from table ColumnFlagsMask ColumnFlags `json:"cols,omitempty"` // inverse flag: 0 == all flags allowed, 0xff == no flags allowed SortColumns []SortColumn `json:"-"` ColumnFlags SliceMap[ColumnFlags] `json:"-"` TerminateEarly bool `json:"-"` // indicate that main() should not actually process any input }
TODO: 2025-02-24 simplify - config should consist of "simple" values, bool, int, string, []string...
func (*Config) AppendSortColumn ¶
type Document ¶
type Document struct {
// contains filtered or unexported fields
}
Document represents a single text file which may contain any number of tables.
This is a model object without any business login.
To Encode or Decode a document, see [document.Encoder] and [document.Decoder]
func (*Document) AppendRow ¶
AppendRow appends a row to the end of the current table.
A new table will be added to the document if necessary.
func (*Document) AppendRuler ¶
func (doc *Document) AppendRuler(prefix, template string, colFlags []ColumnFlags)
AppendRuler appends a ruler to the end of the current table.
A new table will be added to the document if necessary.
func (*Document) AppendText ¶
AppendText appends a line of text to the end of the document.
Text lines are in no-way modified and are reproduced verbatim.
Text lines separate multiple tables within a document.
func (*Document) Items ¶
func (doc *Document) Items() []*DocumentItem
Items returns the slice of all document items within the document
type DocumentItem ¶
DocumentItem represents a single block of text or a table within a document.
If both Text and Table are not nil, then the Text is positioned before the table.
type FieldMap ¶
type FieldMap struct {
// contains filtered or unexported fields
}
FieldMap provides a mapping of field names to their position in a list.
This is used to identify a field in a row, based on a column name.
func NewFieldMap ¶
NewFieldMap returns a new FieldMap for an ordered list of field names.
func (*FieldMap) Find ¶
Find returns the position of a matching string within the FieldMap.
Multiple attempts may be made to find the most appropriate match.
If a name is duplicated, only the first instance will be returned.
If none of the attempts succeed, ok will be false.
Search criteria, in order:
- exact cache match (each search result is memoised for performance)
- exact match
- integer match (always returns ok if >= 1, assumes an infinitely large set of fields)
- case-insensitive exact match
- case-insensitive prefix match
Example ¶
ExampleFieldMap demonstrates how a FieldMap can be used to identify columns of a PSV table
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { fields := []string{"foo", "food", "Footnote", "", "1", "2"} fm := model.NewFieldMap(fields) testCases := []struct{ name, desc string }{ {"foo", "exact match"}, {"food", "exact match"}, {"foot", "case insensitive prefix"}, {"Foot", "case sensitive prefix"}, {"FO", "first case-insensitive prefix"}, {"Fo", "first case-sensitive prefix"}, {"", "empty name - only matches if an empty field exists"}, // searching fields via numbers {"1", "match numeric name"}, {"2", "match numeric name"}, {"+2", "force field number. +2 does not match field name '2', but is a positive integer"}, {"3", "fictitious field"}, {"7", "fictitious field"}, // numeric boundaries {"0", "ignored - field numbers start at 1"}, {"7", "fictitious field"}, } fmt.Printf("Fields: %q\n", fields) for _, tc := range testCases { pos, ok := fm.Find(tc.name) fmt.Printf("find %-6q => %d %v (%s)\n", tc.name, pos, ok, tc.desc) } }
Output: Fields: ["foo" "food" "Footnote" "" "1" "2"] find "foo" => 0 true (exact match) find "food" => 1 true (exact match) find "foot" => 2 true (case insensitive prefix) find "Foot" => 2 true (case sensitive prefix) find "FO" => 0 true (first case-insensitive prefix) find "Fo" => 2 true (first case-sensitive prefix) find "" => 3 true (empty name - only matches if an empty field exists) find "1" => 4 true (match numeric name) find "2" => 5 true (match numeric name) find "+2" => 1 true (force field number. +2 does not match field name '2', but is a positive integer) find "3" => 2 true (fictitious field) find "7" => 6 true (fictitious field) find "0" => 0 false (ignored - field numbers start at 1) find "7" => 6 true (fictitious field)
type Nullable ¶
type Nullable[T comparable] struct { // contains filtered or unexported fields }
func NewNullable ¶
func NewNullable[T comparable](value ...T) Nullable[T]
func (*Nullable[T]) Clear ¶
func (n *Nullable[T]) Clear()
Clear marks the value as NULL, i.e. not valid
func (*Nullable[T]) Coalesce ¶
Coalesce updates the value from another Nullable of the same type, but only if the other Nullable has a non-null value.
func (*Nullable[T]) CoalesceValue ¶
func (n *Nullable[T]) CoalesceValue(values ...T)
Coalesce updates the value from another Nullable of the same type, but only if the other Nullable has a non-null value.
func (Nullable[T]) Get ¶
Get returns the value and its IsSet flag as a tupel.
The value MUST be ignored if the IsSet flag is false.
See also Value()
func (Nullable[T]) MarshalJSON ¶
MarshalJSON encodes the nullable value either in its native JSON form, or as a JSON literal `null`
func (*Nullable[T]) Set ¶
func (n *Nullable[T]) Set(value T)
Set changes the stored value, and marks the value as valid
func (*Nullable[T]) UnmarshalJSON ¶
UnmarshalJSON decodes a JSON string into a nullable value.
If the JSON string is a literal `null`, then the value will be set to NULL (i.e. not valid)
Otherwise, the value will be unmarshalled normally and the resulting value stored as a valid value.
type Ruler ¶
type Ruler struct { Pos int // this ruler's desired position in its table 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.
Rulers grow and shrink depending on the width of the data in each column.
Usage ¶
Building, e.g. while parsing a document
r := &Ruler{} r.Border = … r.Padding = … r.Horizontal = … r.Separator = … err := r.Validate()
Creation from a template
r, err := NewRuler(template)
Ruler Template structure ¶
The template structure was chosen to allow it to be shortened without loss of detail, by being in the same order that they would appear in an actual ruler, while ignoring duplicates.
Template `| -+` ^^^^ |||| outer border --+||| padding ---+|| horizontal line ----+| internal column separator -----+
Example Templates ¶
Template Example Ruler `|-` `|-----|----------|-------|` `|=` `|=====|==========|=======|` `| -` `| --- | -------- | ----- |` (default) `| -+` `| --- + -------- + ----- |` `+ -` `+ --- + -------- + ----- +` `+-` `+-----+----------+-------+`
Example (Zero_value_invalid) ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { // a zero-value ruler is not usable if not validated r := model.Ruler{} fmt.Printf("r := model.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 := model.Ruler{} // without validation Template: "\x00\x00\x00\x00" Border Char: '\x00' Padding Char: '\x00' Horizontal Char: '\x00' Separator Char: '\x00'
Example (Zero_value_validated) ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { // A zero-value ruler IS usable after validation r := model.Ruler{} err := r.Validate() fmt.Printf("r := model.Ruler{}\n") fmt.Printf("r.Validate() // with validation\n") fmt.Printf("Validation Error: %v\n", err) 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 := model.Ruler{} r.Validate() // with validation Validation Error: <nil> Template: "| -|" Border Char: '|' Padding Char: ' ' Horizontal Char: '-' Separator Char: '|'
func NewRuler ¶
NewRuler creates a new ruler from a template string. See Ruler for template construction rules.
The template must be <= 4 characters long.
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
- the separator will be copied from the border
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/model" ) 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 `+ x`, // `+ |`, // vertical line instead of a horizontal line `| =x`, // `| ==`, // horizontal line instead of a vertical line `| -|x`, // too long } for _, t := range badTemplates { _, err := model.NewRuler(t) fmt.Printf("r, err := NewRuler(%-7q) => error: %q\n", t, err) } }
Output: r, err := NewRuler("x" ) => error: "bad vertical ruler character for border 'x'" r, err := NewRuler("-" ) => error: "bad vertical ruler character for border '-'" r, err := NewRuler("+x" ) => error: "bad horizontal ruler character for padding 'x'" r, err := NewRuler("++" ) => error: "bad horizontal ruler character for padding '+'" r, err := NewRuler("+ x" ) => error: "bad horizontal ruler character for horizontal line 'x'" r, err := NewRuler("+ |" ) => error: "bad horizontal ruler character for horizontal line '|'" r, err := NewRuler("| =x" ) => error: "bad vertical ruler character for internal separators 'x'" r, err := NewRuler("| ==" ) => error: "bad vertical ruler character for internal separators '='" r, err := NewRuler("| -|x") => error: "ruler template is too long \"| -|x\" (max 4 characters)"
Example (Default) ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { r, err := model.NewRuler("") fmt.Printf("r, err := model.NewRuler(%q) // 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, err := model.NewRuler("") // 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/model" ) func main() { goodTemplates := []struct { template string notes string }{ {"", "default"}, {"|", "overlaps with default"}, {"+", "minimal replacement"}, {"+-", "unpadded"}, {"|=", "unpadded, emphasised"}, {"| =", "emphasised"}, {"+ -|", "custom"}, } for _, t := range goodTemplates { r, err := model.NewRuler(t.template) if err != nil { fmt.Printf("r, err := NewRuler(%-6q) => unexpected error: %q\n", t.template, err) continue } fmt.Printf("r, err := NewRuler(%-6q) => %q %s\n", t.template, r.Template(), t.notes) } }
Output: r, err := NewRuler("" ) => "| -|" default r, err := NewRuler("|" ) => "| -|" overlaps with default r, err := NewRuler("+" ) => "+ -+" minimal replacement r, err := NewRuler("+-" ) => "+--+" unpadded r, err := NewRuler("|=" ) => "|==|" unpadded, emphasised r, err := NewRuler("| =" ) => "| =|" emphasised r, err := NewRuler("+ -|") => "+ -|" custom
func (Ruler) MarshalJSON ¶
MarshalJSON encodes the ruler to JSON.
Optimisations:
- position is not included if 0
- template is not included if default
- only uses short keys: `{"p":…, "t":…}`
Used mostly for encoding rulers as JSON for tests.
func (*Ruler) String ¶
String returns a string representation of the ruler, consisting of its position in the table and its template
func (*Ruler) UnmarshalJSON ¶
UnmarshalJSON decodes a json string into a ruler.
Keys:
`p` or `pos` for the ruler's position with the table `t` or `template` for the ruler's template
Used mostly for decoding rulers from JSON for tests.
type SliceMap ¶
type SliceMap[T comparable] struct { Data []T // contains filtered or unexported fields }
SliceMap works like a map[int]T, but uses a slice as its underlying data structure.
This is only intended for "small" slices, where the additional cost of using a map is not worth it™ (for your value of "worth it")
According to my testing on an Apple M1, SliceMap is about 5 times faster for slices with on the order of 10 items, compared to using a normal map. This gain drops however with larger slices/maps.
- items can be added with any index - any index can be "retrieved", even if it does not exist in the SliceMap - getting unset items simply returns the type's zero-value
func (SliceMap[T]) Cap ¶
Cap returns the most items currently maintained by the SliceMap
This has limited utility, because calling Set(1000,x) on an empty SliceMap will cause the SliceMap to have a capacity of 1000 even though the SliceMap only contains 1 actual value.
However, the behaviour for retrieving values is always the same, regardless of the actual capacity of the SliceMap's internal slice.
func (*SliceMap[T]) Delete ¶
Delete removes a value from the SliceMap (by setting it to the type's zero-value)
This is equivalent to delete
aMap[i]
func (SliceMap[T]) Get ¶
Get returns an item from the SliceMap allong with an indication of whether the value is within the stored index range.
This is equivalent to v, ok := aMap[i]
func (*SliceMap[T]) Grow ¶
Grow assures that the SliceMap has room for at least a known number of elements.
Calling Grow is not required, but may provide better performance.
func (SliceMap[T]) Has ¶
Has returns true if the provided index is within the range of stored values.
This does NOT indicate whether the specific item was actually set because calling Set(1000,x) on an empty SliceMap will cause the Has to return true for all indexes between 0 and 1000
See also Nullable
func (*SliceMap[T]) Reset ¶
func (s *SliceMap[T]) Reset()
Reset discards all items and releases the memory used by the SliceMap.
func (*SliceMap[T]) Set ¶
Set stores a value in the SliceMap
This is equivalent to
aMap[i] = v
If called with multiple values, then a series of values is set.
The following code blocks are equivalent:
sm.Set( 5, "a", "b", "c" ) sm.Set( 5, "a" ) sm.Set( 6, "b" ) sm.Set( 7, "c" ) aMap[5] = "a" aMap[6] = "b" aMap[7] = "c"
type SortColumn ¶
type Table ¶
type Table struct { Prefix Nullable[string] // prefix of first row in table (optional) ColumnFlags []ColumnFlags // column configurations (optional) Rulers []*Ruler // rulers to intersperse between the tables data rows Rows [][]string // the table's data, without rulers }
Table represents a single, 2-dimensional table of string data
Example (Lookup_fields_by_name) ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { tbl := &model.Table{} tbl.AppendRow([]string{"name", "score"}) tbl.AppendRow([]string{"Adam", "6"}) m := model.NewFieldMap(tbl.ColumnNames()) // generic index of name => field number value := model.LookupFunc[string](m) // func to get field by name from []string for _, row := range tbl.DataRows() { name := value(row, "name") score := value(row, "score") fmt.Printf("name: %q, score: %s\n", name, score) } }
Output: name: "Adam", score: 6
Example (Set_prefix_once) ¶
package main import ( "fmt" "strings" "codeberg.org/japh/psv/encoding/prefix" "codeberg.org/japh/psv/model" ) func main() { input := ` want indent of 8 spaces not 4 spaces and definitely not 12 ` ind := &prefix.Matcher{} tbl := &model.Table{} for _, line := range strings.Split(input, "\n") { if line == "" { continue } p, _ := ind.SplitLine(line) tbl.Prefix.CoalesceValue(p) } fmt.Printf("final indent: %q (%d spaces)\n", tbl.Prefix.Value(), len(tbl.Prefix.Value())) }
Output: final indent: " " (8 spaces)
func (*Table) AllRows ¶
Example ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { tbl := &model.Table{} tbl.AppendRow([]string{"name", "score"}) tbl.AppendRow([]string{"Adam", "6"}) for r, row := range tbl.AllRows() { fmt.Printf("%d: %q\n", r, row) } }
Output: 0: ["name" "score"] 1: ["Adam" "6"]
func (*Table) AppendRow ¶
Example ¶
ExampleTable_AppendRow demonstrates how a table can be built by repeatedly appending rows of data.
Special Cases:
- the number of columns can vary, the table will end up with the maximum number of columns found in any row.
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { tbl := &model.Table{} tbl.AppendRow([]string{"hi"}) // 1 column of data tbl.AppendRow([]string{"hi", "back"}) // 2 columns of data tbl.AppendRow([]string{}) // no data tbl.AppendRow([]string{"see", "ya", "later"}) // 3 columns tbl.AppendRow([]string{"bye"}) // 1 column fmt.Printf("table has %d rows\n", len(tbl.AllRows())) }
Output: table has 5 rows
func (*Table) AppendRuler ¶
Example ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { tbl := &model.Table{} tbl.AppendRuler("") // rulers can be placed anywhere, even before the first row of data tbl.AppendRuler("") // the same ruler can be re-used tbl.AppendRow([]string{"name", "score"}) // 1st row of data, typically column names tbl.AppendRuler("") // a ruler after the first row of data is required for Markdown tbl.AppendRow([]string{"Alice", "5"}) // normal data tbl.AppendRuler("") // tbl.AppendRow([]string{"Bob", "2"}) // normal data tbl.AppendRuler("") // trailing rulers are also no problem fmt.Printf("%d rulers\n", len(tbl.Rulers)) fmt.Printf("%d rows\n", len(tbl.Rows)) }
Output: 5 rulers 3 rows
func (*Table) ColumnNames ¶
Example ¶
package main import ( "fmt" "codeberg.org/japh/psv/model" ) func main() { tbl := &model.Table{} tbl.AppendRow([]string{"name", "score"}) tbl.AppendRow([]string{"Adam", "6"}) names := tbl.ColumnNames() fmt.Printf("column names: %q\n", names) }
Output: column names: ["name" "score"]
func (*Table) DataRows ¶
Example ¶
package main import ( "fmt" "strings" "codeberg.org/japh/psv/model" ) func main() { row := strings.Fields // helper tbl := &model.Table{} tbl.AppendRow(row("name score")) tbl.AppendRow(row("Adam 6 ")) for r, row := range tbl.DataRows() { fmt.Printf("%d: %q\n", r, row) } }
Output: 0: ["Adam" "6"]