docs, builtin, encoding.csv: update error implementations (#13440)

pull/13449/head
Tim Basel 2022-02-12 10:54:10 +01:00 committed by GitHub
parent ae0e90f5d8
commit 37c151efe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 37 deletions

View File

@ -113,6 +113,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
* [Sum types](#sum-types)
* [Type aliases](#type-aliases)
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
* [Custom error types](#custom-error-types)
* [Generics](#generics)
* [Concurrency](#concurrency)
* [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
`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
```v wip

View File

@ -35,8 +35,9 @@ pub fn (err IError) str() string {
}
else {
// >> Hack to allow old style custom error implementations
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
if !isnil(err.msg) {
// TODO: remove once deprecation period for `IError` methods has ended
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'
} else {
// <<
@ -47,13 +48,7 @@ pub fn (err IError) str() string {
}
// Error is the empty default implementation of `IError`.
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 struct Error {}
pub fn (err Error) msg() string {
return ''

View File

@ -26,8 +26,9 @@ pub fn (err IError) str() string {
}
else {
// >> Hack to allow old style custom error implementations
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
if !isnil(err.msg) {
// TODO: remove once deprecation period for `IError` methods has ended
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'
} else {
// <<
@ -38,13 +39,7 @@ pub fn (err IError) str() string {
}
// Error is the empty default implementation of `IError`.
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 struct Error {}
pub fn (err Error) msg() string {
return ''

View File

@ -6,24 +6,36 @@ module csv
// 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
// would then satisfy that interface. I designed it this way to be easily adapted.
struct ErrCommentIsDelimiter {
msg string = 'encoding.csv: comment cannot be the same as delimiter'
code int
struct CommentIsDelimiterError {
Error
}
struct ErrInvalidDelimiter {
msg string = 'encoding.csv: invalid delimiter'
code int
fn (err CommentIsDelimiterError) msg() string {
return 'encoding.csv: comment cannot be the same as delimiter'
}
struct ErrEndOfFile {
msg string = 'encoding.csv: end of file'
code int
struct InvalidDelimiterError {
Error
}
struct ErrInvalidLineEnding {
msg string = 'encoding.csv: could not find any valid line endings'
code int
fn (err InvalidDelimiterError) msg() string {
return 'encoding.csv: invalid delimiter'
}
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 {
@ -72,7 +84,7 @@ pub fn (mut r Reader) read() ?[]string {
fn (mut r Reader) read_line() ?string {
// last record
if r.row_pos == r.data.len {
return IError(&ErrEndOfFile{})
return IError(&EndOfFileError{})
}
le := if r.is_mac_pre_osx_le { '\r' } else { '\n' }
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
} else {
// no valid line endings found
return IError(&ErrInvalidLineEnding{})
return IError(&InvalidLineEndingError{})
}
} else {
// No line ending on file
@ -102,10 +114,10 @@ fn (mut r Reader) read_line() ?string {
fn (mut r Reader) read_record() ?[]string {
if r.delimiter == r.comment {
return IError(&ErrCommentIsDelimiter{})
return IError(&CommentIsDelimiterError{})
}
if !valid_delim(r.delimiter) {
return IError(&ErrInvalidDelimiter{})
return IError(&InvalidDelimiterError{})
}
mut need_read := true
mut keep_raw := false
@ -185,7 +197,7 @@ fn (mut r Reader) read_record() ?[]string {
}
}
if i <= -1 && fields.len == 0 {
return IError(&ErrInvalidDelimiter{})
return IError(&InvalidDelimiterError{})
}
}
return fields

View File

@ -23,7 +23,7 @@ pub fn new_writer() &Writer {
// write writes a single record
pub fn (mut w Writer) write(record []string) ?bool {
if !valid_delim(w.delimiter) {
return IError(&ErrInvalidDelimiter{})
return IError(&InvalidDelimiterError{})
}
le := if w.use_crlf { '\r\n' } else { '\n' }
for n, field_ in record {

View File

@ -1,6 +1,14 @@
struct MyError {
code int
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 {