-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresource_management_file.go
142 lines (112 loc) · 4.29 KB
/
resource_management_file.go
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
package main
import (
"io/ioutil"
"os"
)
const NewFileFlag = os.O_CREATE | os.O_WRONLY
const OwnerRWOnly os.FileMode = 0600
func writeFile1(path, content string) error {
file, err := os.OpenFile(path, NewFileFlag, OwnerRWOnly) // 1
if err != nil { // boilerplate //
return err //
} //
// we should know that we should call file.Close() //
defer file.Close() // error is not handled // 4 (!)
//
_, err = file.Write([]byte(content)) // 2
if err != nil { //
return err //
} //
// write more content //
_, err = file.Write([]byte(content)) // 3
//
return err // 5
}
func writeFile1_WithErrorHandling(path, content string) error {
file, err := os.OpenFile(path, NewFileFlag, OwnerRWOnly) // 1
if err != nil { //
return err //
} //
defer func() { //
closeErr := file.Close() // 4
if err == nil { //
err = closeErr // 5
} //
}() //
//
_, err = file.Write([]byte(content)) // 2
if err != nil { //
return err //
} //
// write more content //
_, err = file.Write([]byte(content)) // 3
//
return err // 6 returns err from (3) or closeErr from (4~5)
}
// What is bad about resource management here?
/*
* You have to remember to close resource -> You can forget
* You have to know how to do it -> You can make a mistake
*/
// The solution
/*
Extract resource management to a separate function:
* inverse control
* make intuitive nesting
* handle error by design
*/
func writeFile2(path, content string) error {
return NewFileResource(path, NewFileFlag, OwnerRWOnly)(
func(file *os.File) error {
_, err := file.Write([]byte(content))
if err != nil {
return err
}
// more content to the God of content
_, err = file.Write([]byte(content))
return err
},
)
}
func writeFile3(fr FileResource, content string) error {
return fr(func(file *os.File) error {
_, err := file.Write([]byte(content))
if err != nil {
return err
}
// more content to the God of content
_, err = file.Write([]byte(content))
return err
})
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
type FileResourceCallback = func (fd *os.File) error
type FileResource = func(callback FileResourceCallback) error
// func NewFileResource(path string, flags int, perm os.FileMode, callback FileResourceCallback) error {
func NewFileResource(path string, flags int, perm os.FileMode) FileResource {
return func(callback FileResourceCallback) error {
file, err := os.OpenFile(path, flags, perm)
if err != nil {
return err
}
err = callback(file)
if err != nil {
// try to close, but return user's error anyway
// or maybe combine in one error
_ = file.Close()
return err
} else {
return file.Close()
}
}
}
var TempFileResource FileResource =
func(callback FileResourceCallback) error {
file, err := ioutil.TempFile("", "")
if err != nil {
return err
}
defer file.Close()
defer os.Remove(file.Name())
return callback(file)
}