-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoc.go
More file actions
158 lines (158 loc) · 4.85 KB
/
Copy pathdoc.go
File metadata and controls
158 lines (158 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Package csvpp implements the IETF CSV++ specification (draft-mscaldas-csvpp-02).
//
// CSV++ extends traditional CSV to support arrays and structured fields within cells,
// enabling complex data representation while maintaining CSV's simplicity.
// This package wraps encoding/csv and is fully compatible with RFC 4180.
//
// # Overview
//
// CSV++ introduces four field types beyond simple text values:
//
// - Simple: "name" - plain text value
// - Array: "tags[]" - multiple values separated by a delimiter (default: ~ at top level only)
// - Structured: "geo(lat^lon)" - named components separated by a delimiter (default: ^)
// - ArrayStructured: "addresses[](street^city)" - array of structured values
//
// Per draft-02, the default tilde (~) delimiter for empty brackets applies only to
// top-level (first-level) arrays. Nested arrays MUST explicitly specify a delimiter.
//
// These field types are represented by the [FieldKind] constants:
// [SimpleField], [ArrayField], [StructuredField], and [ArrayStructuredField].
//
// # Basic Usage
//
// Reading CSV++ data:
//
// r := csvpp.NewReader(file)
//
// // Get parsed headers
// headers, err := r.Headers()
// if err != nil {
// log.Fatal(err)
// }
//
// // Read records
// for {
// record, err := r.Read()
// if err == io.EOF {
// break
// }
// if err != nil {
// log.Fatal(err)
// }
// // process record
// }
//
// Writing CSV++ data:
//
// w := csvpp.NewWriter(file)
// w.SetHeaders(headers)
//
// if err := w.WriteHeader(); err != nil {
// log.Fatal(err)
// }
//
// for _, record := range records {
// if err := w.Write(record); err != nil {
// log.Fatal(err)
// }
// }
// w.Flush()
// if err := w.Error(); err != nil {
// log.Fatal(err)
// }
//
// # Struct Mapping
//
// Use Marshal and Unmarshal for automatic struct mapping with struct tags:
//
// type Person struct {
// Name string `csvpp:"name"`
// Phones []string `csvpp:"phone[]"`
// Geo struct {
// Lat string
// Lon string
// } `csvpp:"geo(lat^lon)"`
// }
//
// // Read into structs
// var people []Person
// if err := csvpp.Unmarshal(file, &people); err != nil {
// log.Fatal(err)
// }
//
// // Write from structs
// var buf bytes.Buffer
// if err := csvpp.Marshal(&buf, people); err != nil {
// log.Fatal(err)
// }
//
// # Delimiter Conventions
//
// The IETF CSV++ specification recommends using specific delimiters for nested structures
// to avoid conflicts. The recommended progression is:
//
// - Level 1 (arrays): ~ (tilde)
// - Level 2 (components): ^ (caret)
// - Level 3: ; (semicolon)
// - Level 4: : (colon)
//
// This package uses ~ and ^ as defaults, matching the IETF recommendation.
//
// # Compatibility with encoding/csv
//
// This package wraps encoding/csv and inherits its RFC 4180 compliance.
// The Reader and Writer types expose the same configuration options:
//
// - Comma: field delimiter (default: ',')
// - Comment: comment character (Reader only)
// - LazyQuotes: relaxed quote handling (Reader only)
// - TrimLeadingSpace: trim leading whitespace (Reader only)
// - UseCRLF: use \r\n line endings (Writer only)
//
// # Security Considerations
//
// The MaxNestingDepth option (default: 10) limits the depth of nested structures
// to prevent stack overflow attacks from maliciously crafted input.
//
// # CSV Injection
//
// When CSV files are opened in spreadsheet applications (Excel, Google Sheets, etc.),
// values beginning with '=', '+', '-', or '@' may be interpreted as formulas.
// This can lead to security vulnerabilities known as "CSV injection" or "formula injection".
//
// Use the [HasFormulaPrefix] function to detect potentially dangerous values:
//
// for _, field := range record {
// if csvpp.HasFormulaPrefix(field.Value) {
// field.Value = "'" + field.Value // Escape for spreadsheet safety
// }
// }
//
// Note: This package does not automatically escape formula prefixes to preserve
// data integrity. Applications should implement appropriate escaping based on
// their specific security requirements and target environments.
//
// # Errors
//
// The package defines the following sentinel errors:
//
// - [ErrNoHeader]: returned when attempting to read without a header row
// - [ErrInvalidHeader]: returned when header format is invalid
// - [ErrNestingTooDeep]: returned when nesting exceeds MaxNestingDepth
//
// Parse errors are wrapped in [ParseError], which provides line/column information.
//
// # Constants
//
// Default delimiters follow IETF recommendations:
//
// - [DefaultArrayDelimiter]: ~ (tilde) for array fields
// - [DefaultComponentDelimiter]: ^ (caret) for structured fields
// - [DefaultMaxNestingDepth]: 10 (IETF recommended limit)
//
// # Specification Reference
//
// For the complete IETF CSV++ specification, see:
// https://datatracker.ietf.org/doc/draft-mscaldas-csvpp/
package csvpp