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) * [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

View File

@ -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 ''

View File

@ -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 ''

View File

@ -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

View File

@ -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 {

View File

@ -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 {