docs, builtin, encoding.csv: update error implementations (#13440)
parent
ae0e90f5d8
commit
37c151efe5
34
doc/docs.md
34
doc/docs.md
|
@ -113,6 +113,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
|
||||||
* [Sum types](#sum-types)
|
* [Sum types](#sum-types)
|
||||||
* [Type aliases](#type-aliases)
|
* [Type aliases](#type-aliases)
|
||||||
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
|
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
|
||||||
|
* [Custom error types](#custom-error-types)
|
||||||
* [Generics](#generics)
|
* [Generics](#generics)
|
||||||
* [Concurrency](#concurrency)
|
* [Concurrency](#concurrency)
|
||||||
* [Spawning Concurrent Tasks](#spawning-concurrent-tasks)
|
* [Spawning Concurrent Tasks](#spawning-concurrent-tasks)
|
||||||
|
@ -3351,6 +3352,39 @@ if resp := http.get('https://google.com') {
|
||||||
Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the first
|
Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the first
|
||||||
`if` branch. `err` is only in scope for the `else` branch.
|
`if` branch. `err` is only in scope for the `else` branch.
|
||||||
|
|
||||||
|
|
||||||
|
## Custom error types
|
||||||
|
|
||||||
|
V gives you the ability to define custom error types through the `IError` interface.
|
||||||
|
The interface requires two methods: `msg() string` and `code() int`. Every type that
|
||||||
|
implements these methods can be used as an error.
|
||||||
|
|
||||||
|
When defining a custom error type it is recommended to embed the builtin `Error` default
|
||||||
|
implementation. This provides an empty default implementation for both required methods,
|
||||||
|
so you only have to implement what you really need, and may provide additional utility
|
||||||
|
functions in the future.
|
||||||
|
|
||||||
|
```v
|
||||||
|
struct PathError {
|
||||||
|
Error
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (err PathError) msg() string {
|
||||||
|
return 'Failed to open path: $err.path'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_open(path string) ? {
|
||||||
|
return IError(PathError{
|
||||||
|
path: path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
try_open('/tmp') or { panic(err) }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Generics
|
## Generics
|
||||||
|
|
||||||
```v wip
|
```v wip
|
||||||
|
|
|
@ -35,8 +35,9 @@ pub fn (err IError) str() string {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// >> Hack to allow old style custom error implementations
|
// >> Hack to allow old style custom error implementations
|
||||||
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
|
// TODO: remove once deprecation period for `IError` methods has ended
|
||||||
if !isnil(err.msg) {
|
old_error_style := unsafe { voidptr(&err.msg) != voidptr(&err.code) } // if fields are not defined (new style) they don't have an offset between them
|
||||||
|
if old_error_style {
|
||||||
'$err.type_name(): $err.msg'
|
'$err.type_name(): $err.msg'
|
||||||
} else {
|
} else {
|
||||||
// <<
|
// <<
|
||||||
|
@ -47,13 +48,7 @@ pub fn (err IError) str() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error is the empty default implementation of `IError`.
|
// Error is the empty default implementation of `IError`.
|
||||||
pub struct Error {
|
pub struct Error {}
|
||||||
// >> Hack to allow old style custom error implementations
|
|
||||||
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
|
|
||||||
msg string
|
|
||||||
code int
|
|
||||||
/// <<
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (err Error) msg() string {
|
pub fn (err Error) msg() string {
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -26,8 +26,9 @@ pub fn (err IError) str() string {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// >> Hack to allow old style custom error implementations
|
// >> Hack to allow old style custom error implementations
|
||||||
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
|
// TODO: remove once deprecation period for `IError` methods has ended
|
||||||
if !isnil(err.msg) {
|
old_error_style := unsafe { voidptr(&err.msg) != voidptr(&err.code) } // if fields are not defined (new style) they don't have an offset between
|
||||||
|
if old_error_style {
|
||||||
'$err.type_name(): $err.msg'
|
'$err.type_name(): $err.msg'
|
||||||
} else {
|
} else {
|
||||||
// <<
|
// <<
|
||||||
|
@ -38,13 +39,7 @@ pub fn (err IError) str() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error is the empty default implementation of `IError`.
|
// Error is the empty default implementation of `IError`.
|
||||||
pub struct Error {
|
pub struct Error {}
|
||||||
// >> Hack to allow old style custom error implementations
|
|
||||||
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
|
|
||||||
msg string
|
|
||||||
code int
|
|
||||||
/// <<
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (err Error) msg() string {
|
pub fn (err Error) msg() string {
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -6,24 +6,36 @@ module csv
|
||||||
// Once interfaces are further along the idea would be to have something similar to
|
// Once interfaces are further along the idea would be to have something similar to
|
||||||
// go's io.reader & bufio.reader rather than reading the whole file into string, this
|
// go's io.reader & bufio.reader rather than reading the whole file into string, this
|
||||||
// would then satisfy that interface. I designed it this way to be easily adapted.
|
// would then satisfy that interface. I designed it this way to be easily adapted.
|
||||||
struct ErrCommentIsDelimiter {
|
struct CommentIsDelimiterError {
|
||||||
msg string = 'encoding.csv: comment cannot be the same as delimiter'
|
Error
|
||||||
code int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ErrInvalidDelimiter {
|
fn (err CommentIsDelimiterError) msg() string {
|
||||||
msg string = 'encoding.csv: invalid delimiter'
|
return 'encoding.csv: comment cannot be the same as delimiter'
|
||||||
code int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ErrEndOfFile {
|
struct InvalidDelimiterError {
|
||||||
msg string = 'encoding.csv: end of file'
|
Error
|
||||||
code int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ErrInvalidLineEnding {
|
fn (err InvalidDelimiterError) msg() string {
|
||||||
msg string = 'encoding.csv: could not find any valid line endings'
|
return 'encoding.csv: invalid delimiter'
|
||||||
code int
|
}
|
||||||
|
|
||||||
|
struct EndOfFileError {
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (err EndOfFileError) msg() string {
|
||||||
|
return 'encoding.csv: end of file'
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InvalidLineEndingError {
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (err InvalidLineEndingError) msg() string {
|
||||||
|
return 'encoding.csv: could not find any valid line endings'
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Reader {
|
struct Reader {
|
||||||
|
@ -72,7 +84,7 @@ pub fn (mut r Reader) read() ?[]string {
|
||||||
fn (mut r Reader) read_line() ?string {
|
fn (mut r Reader) read_line() ?string {
|
||||||
// last record
|
// last record
|
||||||
if r.row_pos == r.data.len {
|
if r.row_pos == r.data.len {
|
||||||
return IError(&ErrEndOfFile{})
|
return IError(&EndOfFileError{})
|
||||||
}
|
}
|
||||||
le := if r.is_mac_pre_osx_le { '\r' } else { '\n' }
|
le := if r.is_mac_pre_osx_le { '\r' } else { '\n' }
|
||||||
mut i := r.data.index_after(le, r.row_pos)
|
mut i := r.data.index_after(le, r.row_pos)
|
||||||
|
@ -84,7 +96,7 @@ fn (mut r Reader) read_line() ?string {
|
||||||
r.is_mac_pre_osx_le = true
|
r.is_mac_pre_osx_le = true
|
||||||
} else {
|
} else {
|
||||||
// no valid line endings found
|
// no valid line endings found
|
||||||
return IError(&ErrInvalidLineEnding{})
|
return IError(&InvalidLineEndingError{})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No line ending on file
|
// No line ending on file
|
||||||
|
@ -102,10 +114,10 @@ fn (mut r Reader) read_line() ?string {
|
||||||
|
|
||||||
fn (mut r Reader) read_record() ?[]string {
|
fn (mut r Reader) read_record() ?[]string {
|
||||||
if r.delimiter == r.comment {
|
if r.delimiter == r.comment {
|
||||||
return IError(&ErrCommentIsDelimiter{})
|
return IError(&CommentIsDelimiterError{})
|
||||||
}
|
}
|
||||||
if !valid_delim(r.delimiter) {
|
if !valid_delim(r.delimiter) {
|
||||||
return IError(&ErrInvalidDelimiter{})
|
return IError(&InvalidDelimiterError{})
|
||||||
}
|
}
|
||||||
mut need_read := true
|
mut need_read := true
|
||||||
mut keep_raw := false
|
mut keep_raw := false
|
||||||
|
@ -185,7 +197,7 @@ fn (mut r Reader) read_record() ?[]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if i <= -1 && fields.len == 0 {
|
if i <= -1 && fields.len == 0 {
|
||||||
return IError(&ErrInvalidDelimiter{})
|
return IError(&InvalidDelimiterError{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fields
|
return fields
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub fn new_writer() &Writer {
|
||||||
// write writes a single record
|
// write writes a single record
|
||||||
pub fn (mut w Writer) write(record []string) ?bool {
|
pub fn (mut w Writer) write(record []string) ?bool {
|
||||||
if !valid_delim(w.delimiter) {
|
if !valid_delim(w.delimiter) {
|
||||||
return IError(&ErrInvalidDelimiter{})
|
return IError(&InvalidDelimiterError{})
|
||||||
}
|
}
|
||||||
le := if w.use_crlf { '\r\n' } else { '\n' }
|
le := if w.use_crlf { '\r\n' } else { '\n' }
|
||||||
for n, field_ in record {
|
for n, field_ in record {
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
struct MyError {
|
struct MyError {
|
||||||
code int
|
|
||||||
msg string
|
msg string
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (err MyError) msg() string {
|
||||||
|
return err.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (err MyError) code() int {
|
||||||
|
return err.code
|
||||||
}
|
}
|
||||||
|
|
||||||
fn foo() int | none | IError {
|
fn foo() int | none | IError {
|
||||||
|
|
Loading…
Reference in New Issue