From 0d114aaae5155cdacd211f2169b9d3209a2f8bc4 Mon Sep 17 00:00:00 2001 From: tiendc Date: Fri, 2 Aug 2024 19:59:23 +0700 Subject: [PATCH] Add Go docs for functions and types --- csvlib.go | 1 + decoder.go | 42 ++++++++++++++++++++++++----------------- encoder.go | 13 ++++++++++--- errors.go | 35 ++++++++++++++++++++++++++++++++++ errors_render.go | 14 ++++++++++---- errors_render_as_csv.go | 9 +++++++-- inline_column.go | 2 ++ processors.go | 27 ++++++++++++++++++-------- validator.go | 18 +++++++++--------- 9 files changed, 118 insertions(+), 43 deletions(-) diff --git a/csvlib.go b/csvlib.go index b510d8c..91d9438 100644 --- a/csvlib.go +++ b/csvlib.go @@ -53,6 +53,7 @@ type LocalizationFunc func(key string, params ParameterMap) (string, error) // OnCellErrorFunc function to be called when error happens on decoding cell value type OnCellErrorFunc func(e *CellError) +// ColumnDetail details of a column parsed from a struct tag type ColumnDetail struct { Name string Optional bool diff --git a/decoder.go b/decoder.go index 508a81b..8d06fea 100644 --- a/decoder.go +++ b/decoder.go @@ -12,24 +12,26 @@ import ( "github.com/tiendc/gofn" ) +// DecodeConfig configuration for decoding CSV data as structs type DecodeConfig struct { - // TagName tag name to parse the struct (default is "csv") + // TagName tag name to parse the struct (default is `csv`) TagName string - // NoHeaderMode indicates the input data have no header (default is "false") + // NoHeaderMode indicates the input data have no header (default is `false`) NoHeaderMode bool - // StopOnError when error occurs, stop the processing (default is "true") + // StopOnError when error occurs, stop the processing (default is `true`) StopOnError bool - // TrimSpace trim all cell values before processing (default is "false") + // TrimSpace trim all cell values before processing (default is `false`) TrimSpace bool // RequireColumnOrder order of columns defined in struct must match the order of columns // in the input data (default is "true") RequireColumnOrder bool - // ParseLocalizedHeader header in the input data is localized (default is "false") + // ParseLocalizedHeader header in the input data is localized (default is `false`) + // // For example: // type Student struct { // Name string `csv:"name"` -> `name` is header key now, the actual header is localized based on the key @@ -41,12 +43,14 @@ type DecodeConfig struct { // (default is "false") AllowUnrecognizedColumns bool - // TreatIncorrectStructureAsError treat incorrect data structure as error (default is "true") + // TreatIncorrectStructureAsError treat incorrect data structure as error (default is `true`) + // // For example: header has 5 columns, if there is a row having 6 columns, it will be treated as error // and the decoding process will stop even StopOnError flag is false. TreatIncorrectStructureAsError bool - // DetectRowLine detect exact lines of rows (default is "false") + // DetectRowLine detect exact lines of rows (default is `false`) + // // If turn this flag on, the input reader should be an instance of "encoding/csv" Reader // as this lib uses Reader.FieldPos() function to get the line of a row. DetectRowLine bool @@ -79,7 +83,7 @@ func (c *DecodeConfig) ConfigureColumn(name string, fn func(*DecodeColumnConfig) fn(columnCfg) } -// DecodeColumnConfig configuration for a specific column +// DecodeColumnConfig configuration for decoding a specific column type DecodeColumnConfig struct { // TrimSpace if `true` and DecodeConfig.TrimSpace is `false`, only trim space this column // (default is "false") @@ -98,7 +102,7 @@ type DecodeColumnConfig struct { // ValidatorFuncs a list of functions will be called after decoding (optional) ValidatorFuncs []ValidatorFunc - // OnCellErrorFunc function will be called every time an error happens when decode a cell + // OnCellErrorFunc function will be called every time an error happens when decode a cell. // This func can be helpful to set localization key and additional params for the error // to localize the error message later on. (optional) OnCellErrorFunc OnCellErrorFunc @@ -108,8 +112,10 @@ func defaultDecodeColumnConfig() *DecodeColumnConfig { return &DecodeColumnConfig{} } +// DecodeOption function to modify decoding config type DecodeOption func(cfg *DecodeConfig) +// DecodeResult decoding result type DecodeResult struct { totalRow int unrecognizedColumns []string @@ -128,6 +134,7 @@ func (r *DecodeResult) MissingOptionalColumns() []string { return r.missingOptionalColumns } +// Decoder data structure of the default decoder type Decoder struct { r Reader cfg *DecodeConfig @@ -142,6 +149,7 @@ type Decoder struct { colsMeta []*decodeColumnMeta } +// NewDecoder creates a new Decoder object func NewDecoder(r Reader, options ...DecodeOption) *Decoder { cfg := defaultDecodeConfig() for _, opt := range options { @@ -154,8 +162,8 @@ func NewDecoder(r Reader, options ...DecodeOption) *Decoder { } } -// Decode decode input data and store the result in the given variable -// The input var must be a pointer to a slice, e.g. `*[]Student` (recommended) or `*[]*Student` +// Decode decode input data and store the result in the given variable. +// The input var must be a pointer to a slice, e.g. `*[]Student` (recommended) or `*[]*Student`. func (d *Decoder) Decode(v any) (*DecodeResult, error) { if d.finished { return nil, ErrFinished @@ -216,8 +224,8 @@ func (d *Decoder) Decode(v any) (*DecodeResult, error) { return d.result, nil } -// DecodeOne decode the next one row data -// The input var must be a pointer to a struct (e.g. *Student) +// DecodeOne decode the next one row data. +// The input var must be a pointer to a struct (e.g. *Student). // This func returns error of the current row processing only, after finishing the last row decoding, // call Finish() to get the overall result and error. func (d *Decoder) DecodeOne(v any) error { @@ -271,8 +279,8 @@ func (d *Decoder) Finish() (*DecodeResult, error) { return d.result, nil } -// prepareDecode prepare for decoding by parsing the struct tags and build column decoders -// This step is performed one time only before the first row decoding +// prepareDecode prepare for decoding by parsing the struct tags and build column decoders. +// This step is performed one time only before the first row decoding. func (d *Decoder) prepareDecode(v reflect.Value) error { d.result = &DecodeResult{} itemType, err := d.parseOutputVar(v) @@ -446,7 +454,7 @@ func (d *Decoder) parseOutputVarOne(v reflect.Value) (val reflect.Value, itemTyp return } -// readRowData read data of all rows from the input to struct type +// readRowData read data of all rows from the input to struct type. // If you use `encoding/csv` Reader, we can determine the lines of rows (via Reader.FieldPos func). // Otherwise, `line` will be set to `-1` which mean undetected. func (d *Decoder) readRowData() error { @@ -814,7 +822,7 @@ func (d *Decoder) parseDynamicInlineColumns(colsMetaFromStruct []*decodeColumnMe return newColsMetaFromStruct, nil } -// buildColumnDecoders build decoders for each column type +// buildColumnDecoders build decoders for each column type. // If the type of column is determined, e.g. `int`, the decode function for that will be determined at // the prepare step, and it will be fast at decoding. If it is `interface`, the decode function will parse // the actual type at decoding, and it will be slower. diff --git a/encoder.go b/encoder.go index 5d0742c..8c397e4 100644 --- a/encoder.go +++ b/encoder.go @@ -9,6 +9,7 @@ import ( "github.com/tiendc/gofn" ) +// EncodeConfig configuration for encoding Go structs as CSV data type EncodeConfig struct { // TagName tag name to parse the struct (default is `csv`) TagName string @@ -32,6 +33,7 @@ func defaultEncodeConfig() *EncodeConfig { } } +// ConfigureColumn configures encoding for a column by name func (c *EncodeConfig) ConfigureColumn(name string, fn func(*EncodeColumnConfig)) { if c.columnConfigMap == nil { c.columnConfigMap = map[string]*EncodeColumnConfig{} @@ -44,9 +46,10 @@ func (c *EncodeConfig) ConfigureColumn(name string, fn func(*EncodeColumnConfig) fn(columnCfg) } +// EncodeColumnConfig configuration for encoding a specific column type EncodeColumnConfig struct { // Skip whether skip encoding the column or not (this is equivalent to use `csv:"-"` in struct tag) - // (default is "false") + // (default is `false`) Skip bool // EncodeFunc custom encode function (optional) @@ -60,8 +63,10 @@ func defaultEncodeColumnConfig() *EncodeColumnConfig { return &EncodeColumnConfig{} } +// EncodeOption function to modify encoding config type EncodeOption func(cfg *EncodeConfig) +// Encoder data structure of the default encoder type Encoder struct { w Writer cfg *EncodeConfig @@ -74,6 +79,7 @@ type Encoder struct { colsMeta []*encodeColumnMeta } +// NewEncoder creates a new Encoder object func NewEncoder(w Writer, options ...EncodeOption) *Encoder { cfg := defaultEncodeConfig() for _, opt := range options { @@ -85,8 +91,8 @@ func NewEncoder(w Writer, options ...EncodeOption) *Encoder { } } -// Encode encode input data stored in the given variable -// The input var must be a slice, e.g. `[]Student` or `[]*Student` +// Encode encode input data stored in the given variable. +// The input var must be a slice, e.g. `[]Student` or `[]*Student`. func (e *Encoder) Encode(v any) error { if e.finished { return ErrFinished @@ -162,6 +168,7 @@ func (e *Encoder) EncodeOne(v any) error { return nil } +// Finish encoding, after calling this func, you can't encode more func (e *Encoder) Finish() error { e.finished = true return e.err diff --git a/errors.go b/errors.go index f1600ec..6ecb593 100644 --- a/errors.go +++ b/errors.go @@ -48,32 +48,39 @@ var ( ErrEncodeValueType = errors.New("ErrEncodeValueType") ) +// Errors represents errors returned by the encoder or decoder type Errors struct { // nolint: errname errs []error totalRow int header []string } +// NewErrors creates a new Errors object func NewErrors() *Errors { return &Errors{} } +// TotalRow gets total rows of CSV data func (e *Errors) TotalRow() int { return e.totalRow } +// Header gets list of column headers func (e *Errors) Header() []string { return e.header } +// Error implements Go error interface func (e *Errors) Error() string { return getErrorMsg(e.errs) } +// HasError checks if there is at least one error in the list func (e *Errors) HasError() bool { return len(e.errs) > 0 } +// TotalRowError gets the total number of error of rows func (e *Errors) TotalRowError() int { c := 0 for _, e := range e.errs { @@ -84,6 +91,7 @@ func (e *Errors) TotalRowError() int { return c } +// TotalCellError gets the total number of error of cells func (e *Errors) TotalCellError() int { c := 0 for _, e := range e.errs { @@ -94,6 +102,7 @@ func (e *Errors) TotalCellError() int { return c } +// TotalError gets the total number of errors including row errors and cell errors func (e *Errors) TotalError() int { c := 0 for _, e := range e.errs { @@ -106,10 +115,12 @@ func (e *Errors) TotalError() int { return c } +// Add appends errors to the list func (e *Errors) Add(errs ...error) { e.errs = append(e.errs, errs...) } +// Is checks if there is at least an error in the list kind of the specified error func (e *Errors) Is(err error) bool { for _, er := range e.errs { if errors.Is(er, err) { @@ -119,40 +130,49 @@ func (e *Errors) Is(err error) bool { return false } +// Unwrap implements Go error unwrap function func (e *Errors) Unwrap() []error { return e.errs } +// RowErrors data structure of error of a row type RowErrors struct { // nolint: errname errs []error row int line int } +// NewRowErrors creates a new RowErrors func NewRowErrors(row, line int) *RowErrors { return &RowErrors{row: row, line: line} } +// Row gets the row contains the error func (e *RowErrors) Row() int { return e.row } +// Line gets the line contains the error (line equals to row in most cases) func (e *RowErrors) Line() int { return e.line } +// Error implements Go error interface func (e *RowErrors) Error() string { return getErrorMsg(e.errs) } +// HasError checks if there is at least one error in the list func (e *RowErrors) HasError() bool { return len(e.errs) > 0 } +// TotalError gets the total number of errors func (e *RowErrors) TotalError() int { return len(e.errs) } +// TotalCellError gets the total number of error of cells func (e *RowErrors) TotalCellError() int { c := 0 for _, e := range e.errs { @@ -163,10 +183,12 @@ func (e *RowErrors) TotalCellError() int { return c } +// Add appends errors to the list func (e *RowErrors) Add(errs ...error) { e.errs = append(e.errs, errs...) } +// Is checks if there is at least an error in the list kind of the specified error func (e *RowErrors) Is(err error) bool { for _, er := range e.errs { if errors.Is(er, err) { @@ -176,10 +198,12 @@ func (e *RowErrors) Is(err error) bool { return false } +// Unwrap implements Go error unwrap function func (e *RowErrors) Unwrap() []error { return e.errs } +// CellError data structure of error of a cell type CellError struct { err error fields map[string]any @@ -190,10 +214,12 @@ type CellError struct { value string } +// NewCellError creates a new CellError func NewCellError(err error, column int, header string) *CellError { return &CellError{err: err, column: column, header: header, fields: map[string]any{}} } +// Error implements Go error interface func (e *CellError) Error() string { if e.err == nil { return "" @@ -201,39 +227,48 @@ func (e *CellError) Error() string { return e.err.Error() } +// Column gets the column of the cell func (e *CellError) Column() int { return e.column } +// Header gets the header of the column func (e *CellError) Header() string { return e.header } +// Value gets the value of the cell func (e *CellError) Value() string { return e.value } +// HasError checks if the error contains an error func (e *CellError) HasError() bool { return e.err != nil } +// Is checks if the inner error is kind of the specified error func (e *CellError) Is(err error) bool { return errors.Is(e.err, err) } +// Unwrap implements Go error unwrap function func (e *CellError) Unwrap() error { return e.err } +// WithParam sets a param of error func (e *CellError) WithParam(k string, v any) *CellError { e.fields[k] = v return e } +// LocalizationKey gets localization key of error func (e *CellError) LocalizationKey() string { return e.localizationKey } +// SetLocalizationKey sets localization key of error func (e *CellError) SetLocalizationKey(k string) { e.localizationKey = k } diff --git a/errors_render.go b/errors_render.go index 8cb1229..f9ae6b3 100644 --- a/errors_render.go +++ b/errors_render.go @@ -12,9 +12,10 @@ const ( ) type ErrorRenderConfig struct { - // HeaderFormatKey header format string + // HeaderFormatKey header format string. // You can use a localization key as the value to force the renderer to translate the key first. // If the translation fails, the original value is used for next step. + // // For example, header format key can be: // - "HEADER_FORMAT_KEY" (a localization key you define in your localization data such as a json file, // HEADER_FORMAT_KEY = "CSV decoding result: total errors is {{.TotalError}}") @@ -31,8 +32,9 @@ type ErrorRenderConfig struct { // {{.Tab}} - tab character HeaderFormatKey string - // RowFormatKey format string for each row + // RowFormatKey format string for each row. // Similar to the header format key, this can be a localization key or a direct string. + // // For example, row format key can be: // - "ROW_FORMAT_KEY" (a localization key you define in your localization data such as a json file) // - "Row {{.Row}} (line {{.Line}}): {{.Error}}" (direct string) @@ -64,7 +66,7 @@ type ErrorRenderConfig struct { // LocalizationFunc function to translate message (optional) LocalizationFunc LocalizationFunc - // CellRenderFunc custom render function for rendering a cell error (optional) + // CellRenderFunc custom render function for rendering a cell error (optional). // The func can return ("", false) to skip rendering the cell error, return ("", true) to let the // renderer continue using its solution, and return ("", true) to override the value. // @@ -96,12 +98,15 @@ func defaultRenderConfig() *ErrorRenderConfig { } } +// SimpleRenderer a simple implementation of error renderer which can produce a text message +// for the input errors. type SimpleRenderer struct { cfg *ErrorRenderConfig sourceErr *Errors transErr error } +// NewRenderer creates a new SimpleRenderer func NewRenderer(err *Errors, options ...func(*ErrorRenderConfig)) (*SimpleRenderer, error) { cfg := defaultRenderConfig() for _, opt := range options { @@ -110,7 +115,8 @@ func NewRenderer(err *Errors, options ...func(*ErrorRenderConfig)) (*SimpleRende return &SimpleRenderer{cfg: cfg, sourceErr: err}, nil } -// Render render Errors object as text +// Render renders Errors object as text. +// // Sample output: // // There are 5 total errors in your CSV file diff --git a/errors_render_as_csv.go b/errors_render_as_csv.go index 9c5e819..b99c19d 100644 --- a/errors_render_as_csv.go +++ b/errors_render_as_csv.go @@ -46,7 +46,7 @@ type CSVRenderConfig struct { // HeaderRenderFunc custom render function for rendering header row (optional) HeaderRenderFunc func([]string, ParameterMap) - // CellRenderFunc custom render function for rendering a cell error (optional) + // CellRenderFunc custom render function for rendering a cell error (optional). // The func can return ("", false) to skip rendering the cell error, return ("", true) to let the // renderer continue using its solution, and return ("", true) to override the value. // @@ -78,6 +78,8 @@ func defaultCSVRenderConfig() *CSVRenderConfig { } } +// CSVRenderer an implementation of error renderer which can produce messages +// for the input errors as CSV output data. type CSVRenderer struct { cfg *CSVRenderConfig sourceErr *Errors @@ -87,6 +89,7 @@ type CSVRenderer struct { data [][]string } +// NewCSVRenderer creates a new CSVRenderer func NewCSVRenderer(err *Errors, options ...func(*CSVRenderConfig)) (*CSVRenderer, error) { cfg := defaultCSVRenderConfig() for _, opt := range options { @@ -113,7 +116,7 @@ func NewCSVRenderer(err *Errors, options ...func(*CSVRenderConfig)) (*CSVRendere return &CSVRenderer{cfg: cfg, sourceErr: err}, nil } -// Render render Errors object as CSV rows data +// Render renders Errors object as CSV rows data func (r *CSVRenderer) Render() (data [][]string, transErr error, err error) { cfg := r.cfg r.startCellErrIndex = 0 @@ -156,6 +159,7 @@ func (r *CSVRenderer) Render() (data [][]string, transErr error, err error) { return r.data, r.transErr, nil } +// RenderAsString renders the input as CSV string func (r *CSVRenderer) RenderAsString() (msg string, transErr error, err error) { csvData, transErr, err := r.Render() if err != nil { @@ -170,6 +174,7 @@ func (r *CSVRenderer) RenderAsString() (msg string, transErr error, err error) { return buf.String(), transErr, nil } +// RenderTo renders the input as CSV string and writes it to the writer func (r *CSVRenderer) RenderTo(w Writer) (transErr error, err error) { csvData, transErr, err := r.Render() if err != nil { diff --git a/inline_column.go b/inline_column.go index dafdc48..9e313d5 100644 --- a/inline_column.go +++ b/inline_column.go @@ -14,11 +14,13 @@ const ( dynamicInlineColumnValues = "Values" ) +// InlineColumn represents inline columns of type `T` type InlineColumn[T any] struct { Header []string Values []T } +// inlineColumnMeta metadata of inline columns type inlineColumnMeta struct { headerText []string inlineType inlineColumnStructType diff --git a/processors.go b/processors.go index 2d4808f..11bed08 100644 --- a/processors.go +++ b/processors.go @@ -7,21 +7,32 @@ import ( ) var ( - ProcessorTrim = strings.TrimSpace - ProcessorTrimPrefix = strings.TrimPrefix - ProcessorTrimSuffix = strings.TrimSuffix - ProcessorReplace = strings.Replace - ProcessorReplaceAll = strings.ReplaceAll - ProcessorLower = strings.ToLower - ProcessorUpper = strings.ToUpper - ProcessorNumberGroup = gofn.NumberFmtGroup + // ProcessorTrim trims space of string + ProcessorTrim = strings.TrimSpace + // ProcessorTrimPrefix trims prefix from a string + ProcessorTrimPrefix = strings.TrimPrefix + // ProcessorTrimSuffix trims suffix from a string + ProcessorTrimSuffix = strings.TrimSuffix + // ProcessorReplace replaces a substring in a string + ProcessorReplace = strings.Replace + // ProcessorReplaceAll replaces all occurrences of a substring in a string + ProcessorReplaceAll = strings.ReplaceAll + // ProcessorLower converts a string to lowercase + ProcessorLower = strings.ToLower + // ProcessorUpper converts a string to uppercase + ProcessorUpper = strings.ToUpper + // ProcessorNumberGroup formats a number with grouping its digits + ProcessorNumberGroup = gofn.NumberFmtGroup + // ProcessorNumberUngroup ungroups number digits ProcessorNumberUngroup = gofn.NumberFmtUngroup ) +// ProcessorNumberGroupComma formats a number with grouping its digits by comma func ProcessorNumberGroupComma(s string) string { return gofn.NumberFmtGroup(s, '.', ',') } +// ProcessorNumberUngroupComma ungroups number digits by comma func ProcessorNumberUngroupComma(s string) string { return gofn.NumberFmtUngroup(s, ',') } diff --git a/validator.go b/validator.go index db9e200..ab71af4 100644 --- a/validator.go +++ b/validator.go @@ -8,7 +8,7 @@ import ( "unsafe" ) -// ValidatorLT validate a value to be less than the given value +// ValidatorLT validates a value to be less than the given value func ValidatorLT[T LTComparable](val T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -22,7 +22,7 @@ func ValidatorLT[T LTComparable](val T) ValidatorFunc { } } -// ValidatorLTE validate a value to be less than or equal to the given value +// ValidatorLTE validates a value to be less than or equal to the given value func ValidatorLTE[T LTComparable](val T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -36,7 +36,7 @@ func ValidatorLTE[T LTComparable](val T) ValidatorFunc { } } -// ValidatorGT validate a value to be greater than the given value +// ValidatorGT validates a value to be greater than the given value func ValidatorGT[T LTComparable](val T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -50,7 +50,7 @@ func ValidatorGT[T LTComparable](val T) ValidatorFunc { } } -// ValidatorGTE validate a value to be greater than or equal to the given value +// ValidatorGTE validates a value to be greater than or equal to the given value func ValidatorGTE[T LTComparable](val T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -64,7 +64,7 @@ func ValidatorGTE[T LTComparable](val T) ValidatorFunc { } } -// ValidatorRange validate a value to be in the given range (min and max are inclusive) +// ValidatorRange validates a value to be in the given range (min and max are inclusive) func ValidatorRange[T LTComparable](min, max T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -78,7 +78,7 @@ func ValidatorRange[T LTComparable](min, max T) ValidatorFunc { } } -// ValidatorIN validate a value to be one of the specific values +// ValidatorIN validates a value to be one of the specific values func ValidatorIN[T LTComparable](vals ...T) ValidatorFunc { return func(v any) error { v1, ok := v.(T) @@ -94,7 +94,7 @@ func ValidatorIN[T LTComparable](vals ...T) ValidatorFunc { } } -// ValidatorStrLen validate a string to have length in the given range +// ValidatorStrLen validates a string to have length in the given range. // Pass argument -1 to skip the equivalent validation. func ValidatorStrLen[T StringEx](minLen, maxLen int, lenFuncs ...func(s string) int) ValidatorFunc { return func(v any) error { @@ -114,7 +114,7 @@ func ValidatorStrLen[T StringEx](minLen, maxLen int, lenFuncs ...func(s string) } } -// ValidatorStrPrefix validate a string to have prefix matching the given one +// ValidatorStrPrefix validates a string to have prefix matching the given one func ValidatorStrPrefix[T StringEx](prefix string) ValidatorFunc { return func(v any) error { s, ok := v.(T) @@ -128,7 +128,7 @@ func ValidatorStrPrefix[T StringEx](prefix string) ValidatorFunc { } } -// ValidatorStrSuffix validate a string to have suffix matching the given one +// ValidatorStrSuffix validates a string to have suffix matching the given one func ValidatorStrSuffix[T StringEx](suffix string) ValidatorFunc { return func(v any) error { s, ok := v.(T)