Skip to content

Commit 37c151e

Browse files
authored
docs, builtin, encoding.csv: update error implementations (#13440)
1 parent ae0e90f commit 37c151e

File tree

6 files changed

+81
-37
lines changed

6 files changed

+81
-37
lines changed

doc/docs.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
113113
* [Sum types](#sum-types)
114114
* [Type aliases](#type-aliases)
115115
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
116+
* [Custom error types](#custom-error-types)
116117
* [Generics](#generics)
117118
* [Concurrency](#concurrency)
118119
* [Spawning Concurrent Tasks](#spawning-concurrent-tasks)
@@ -3351,6 +3352,39 @@ if resp := http.get('https://google.com') {
33513352
Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the first
33523353
`if` branch. `err` is only in scope for the `else` branch.
33533354

3355+
3356+
## Custom error types
3357+
3358+
V gives you the ability to define custom error types through the `IError` interface.
3359+
The interface requires two methods: `msg() string` and `code() int`. Every type that
3360+
implements these methods can be used as an error.
3361+
3362+
When defining a custom error type it is recommended to embed the builtin `Error` default
3363+
implementation. This provides an empty default implementation for both required methods,
3364+
so you only have to implement what you really need, and may provide additional utility
3365+
functions in the future.
3366+
3367+
```v
3368+
struct PathError {
3369+
Error
3370+
path string
3371+
}
3372+
3373+
fn (err PathError) msg() string {
3374+
return 'Failed to open path: $err.path'
3375+
}
3376+
3377+
fn try_open(path string) ? {
3378+
return IError(PathError{
3379+
path: path
3380+
})
3381+
}
3382+
3383+
fn main() {
3384+
try_open('/tmp') or { panic(err) }
3385+
}
3386+
```
3387+
33543388
## Generics
33553389

33563390
```v wip

vlib/builtin/js/builtin.v

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ pub fn (err IError) str() string {
3535
}
3636
else {
3737
// >> Hack to allow old style custom error implementations
38-
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
39-
if !isnil(err.msg) {
38+
// TODO: remove once deprecation period for `IError` methods has ended
39+
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
40+
if old_error_style {
4041
'$err.type_name(): $err.msg'
4142
} else {
4243
// <<
@@ -47,13 +48,7 @@ pub fn (err IError) str() string {
4748
}
4849

4950
// Error is the empty default implementation of `IError`.
50-
pub struct Error {
51-
// >> Hack to allow old style custom error implementations
52-
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
53-
msg string
54-
code int
55-
/// <<
56-
}
51+
pub struct Error {}
5752

5853
pub fn (err Error) msg() string {
5954
return ''

vlib/builtin/option.v

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ pub fn (err IError) str() string {
2626
}
2727
else {
2828
// >> Hack to allow old style custom error implementations
29-
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
30-
if !isnil(err.msg) {
29+
// TODO: remove once deprecation period for `IError` methods has ended
30+
old_error_style := unsafe { voidptr(&err.msg) != voidptr(&err.code) } // if fields are not defined (new style) they don't have an offset between
31+
if old_error_style {
3132
'$err.type_name(): $err.msg'
3233
} else {
3334
// <<
@@ -38,13 +39,7 @@ pub fn (err IError) str() string {
3839
}
3940

4041
// Error is the empty default implementation of `IError`.
41-
pub struct Error {
42-
// >> Hack to allow old style custom error implementations
43-
// TODO: can be removed once the checker 'hacks' are merged (so `vc` has them included)
44-
msg string
45-
code int
46-
/// <<
47-
}
42+
pub struct Error {}
4843

4944
pub fn (err Error) msg() string {
5045
return ''

vlib/encoding/csv/reader.v

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,36 @@ module csv
66
// Once interfaces are further along the idea would be to have something similar to
77
// go's io.reader & bufio.reader rather than reading the whole file into string, this
88
// would then satisfy that interface. I designed it this way to be easily adapted.
9-
struct ErrCommentIsDelimiter {
10-
msg string = 'encoding.csv: comment cannot be the same as delimiter'
11-
code int
9+
struct CommentIsDelimiterError {
10+
Error
1211
}
1312

14-
struct ErrInvalidDelimiter {
15-
msg string = 'encoding.csv: invalid delimiter'
16-
code int
13+
fn (err CommentIsDelimiterError) msg() string {
14+
return 'encoding.csv: comment cannot be the same as delimiter'
1715
}
1816

19-
struct ErrEndOfFile {
20-
msg string = 'encoding.csv: end of file'
21-
code int
17+
struct InvalidDelimiterError {
18+
Error
2219
}
2320

24-
struct ErrInvalidLineEnding {
25-
msg string = 'encoding.csv: could not find any valid line endings'
26-
code int
21+
fn (err InvalidDelimiterError) msg() string {
22+
return 'encoding.csv: invalid delimiter'
23+
}
24+
25+
struct EndOfFileError {
26+
Error
27+
}
28+
29+
fn (err EndOfFileError) msg() string {
30+
return 'encoding.csv: end of file'
31+
}
32+
33+
struct InvalidLineEndingError {
34+
Error
35+
}
36+
37+
fn (err InvalidLineEndingError) msg() string {
38+
return 'encoding.csv: could not find any valid line endings'
2739
}
2840

2941
struct Reader {
@@ -72,7 +84,7 @@ pub fn (mut r Reader) read() ?[]string {
7284
fn (mut r Reader) read_line() ?string {
7385
// last record
7486
if r.row_pos == r.data.len {
75-
return IError(&ErrEndOfFile{})
87+
return IError(&EndOfFileError{})
7688
}
7789
le := if r.is_mac_pre_osx_le { '\r' } else { '\n' }
7890
mut i := r.data.index_after(le, r.row_pos)
@@ -84,7 +96,7 @@ fn (mut r Reader) read_line() ?string {
8496
r.is_mac_pre_osx_le = true
8597
} else {
8698
// no valid line endings found
87-
return IError(&ErrInvalidLineEnding{})
99+
return IError(&InvalidLineEndingError{})
88100
}
89101
} else {
90102
// No line ending on file
@@ -102,10 +114,10 @@ fn (mut r Reader) read_line() ?string {
102114

103115
fn (mut r Reader) read_record() ?[]string {
104116
if r.delimiter == r.comment {
105-
return IError(&ErrCommentIsDelimiter{})
117+
return IError(&CommentIsDelimiterError{})
106118
}
107119
if !valid_delim(r.delimiter) {
108-
return IError(&ErrInvalidDelimiter{})
120+
return IError(&InvalidDelimiterError{})
109121
}
110122
mut need_read := true
111123
mut keep_raw := false
@@ -185,7 +197,7 @@ fn (mut r Reader) read_record() ?[]string {
185197
}
186198
}
187199
if i <= -1 && fields.len == 0 {
188-
return IError(&ErrInvalidDelimiter{})
200+
return IError(&InvalidDelimiterError{})
189201
}
190202
}
191203
return fields

vlib/encoding/csv/writer.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn new_writer() &Writer {
2323
// write writes a single record
2424
pub fn (mut w Writer) write(record []string) ?bool {
2525
if !valid_delim(w.delimiter) {
26-
return IError(&ErrInvalidDelimiter{})
26+
return IError(&InvalidDelimiterError{})
2727
}
2828
le := if w.use_crlf { '\r\n' } else { '\n' }
2929
for n, field_ in record {

vlib/v/tests/string_optional_none_test.v

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
struct MyError {
2-
code int
32
msg string
3+
code int
4+
}
5+
6+
fn (err MyError) msg() string {
7+
return err.msg
8+
}
9+
10+
fn (err MyError) code() int {
11+
return err.code
412
}
513

614
fn foo() int | none | IError {

0 commit comments

Comments
 (0)