-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
io: introduce a go-like io.util module (#6323)
- Loading branch information
Showing
2 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
module util | ||
|
||
import os | ||
import rand | ||
import rand.wyrand | ||
import rand.util as rutil | ||
|
||
const ( | ||
retries = 10000 | ||
) | ||
|
||
pub struct TempFileOptions { | ||
path string = os.temp_dir() | ||
pattern string = '' | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
medvednikov
Member
|
||
} | ||
|
||
// temp_file returns an uniquely named, open, writable, `os.File` and it's path | ||
pub fn temp_file(tfo TempFileOptions) ?(os.File, string) { | ||
mut d := tfo.path | ||
if d == '' { | ||
d = os.temp_dir() | ||
} | ||
os.is_writable_folder(d) or { | ||
return error(@FN + | ||
' could not create temporary file in "$d". Please ensure write permissions.') | ||
} | ||
d = d.trim_right(os.path_separator) | ||
mut rng := rand.new_default(rand.PRNGConfigStruct{}) | ||
prefix, suffix := prefix_and_suffix(tfo.pattern) or { | ||
return error(@FN + ' ' + err) | ||
} | ||
for retry := 0; retry < retries; retry++ { | ||
This comment has been minimized.
Sorry, something went wrong. |
||
path := os.join_path(d, prefix + random_number(mut rng) + suffix) | ||
mut mode := 'rw+' | ||
$if windows { | ||
mode = 'w+' | ||
} | ||
mut file := os.open_file(path, mode, 0o600) or { | ||
rng.seed(rutil.time_seed_array(2)) | ||
continue | ||
} | ||
if os.exists(path) && os.is_file(path) { | ||
return file, path | ||
} | ||
} | ||
return error(@FN + | ||
' could not create temporary file in "$d". Retry limit ($retries) exhausted. Please ensure write permissions.') | ||
} | ||
|
||
pub struct TempDirOptions { | ||
path string = os.temp_dir() | ||
pattern string = '' | ||
} | ||
|
||
// temp_dir returns an uniquely named, writable, directory path | ||
pub fn temp_dir(tdo TempFileOptions) ?string { | ||
mut d := tdo.path | ||
if d == '' { | ||
d = os.temp_dir() | ||
} | ||
os.is_writable_folder(d) or { | ||
return error(@FN + | ||
' could not create temporary directory "$d". Please ensure write permissions.') | ||
} | ||
d = d.trim_right(os.path_separator) | ||
mut rng := rand.new_default(rand.PRNGConfigStruct{}) | ||
prefix, suffix := prefix_and_suffix(tdo.pattern) or { | ||
return error(@FN + ' ' + err) | ||
} | ||
for retry := 0; retry < retries; retry++ { | ||
path := os.join_path(d, prefix + random_number(mut rng) + suffix) | ||
os.mkdir_all(path) or { | ||
rng.seed(rutil.time_seed_array(2)) | ||
continue | ||
} | ||
if os.is_dir(path) && os.exists(path) { | ||
os.is_writable_folder(path) or { | ||
return error(@FN + | ||
' could not create temporary directory "$d". Please ensure write permissions.') | ||
} | ||
return path | ||
} | ||
} | ||
return error(@FN + | ||
' could not create temporary directory "$d". Retry limit ($retries) exhausted. Please ensure write permissions.') | ||
} | ||
|
||
// * Utility functions | ||
fn random_number(mut rng wyrand.WyRandRNG) string { | ||
s := (u32(1e9) + (u32(os.getpid()) + rng.u32() % u32(1e9))).str() | ||
return s.substr(1, s.len) | ||
} | ||
|
||
fn prefix_and_suffix(pattern string) ?(string, string) { | ||
mut pat := pattern | ||
if pat.contains(os.path_separator) { | ||
return error('pattern cannot contain path separators ($os.path_separator).') | ||
} | ||
pos := pat.last_index('*') or { | ||
-1 | ||
} | ||
mut prefix := '' | ||
mut suffix := '' | ||
if pos != -1 { | ||
prefix = pat.substr(0, pos) | ||
suffix = pat.substr(pos + 1, pat.len) | ||
} else { | ||
prefix = pat | ||
} | ||
return prefix, suffix | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import os | ||
import io.util | ||
|
||
const ( | ||
// tfolder will contain all the temporary files/subfolders made by | ||
// the different tests. It would be removed in testsuite_end(), so | ||
// individual os tests do not need to clean up after themselves. | ||
tfolder = os.join_path(os.temp_dir(), 'v', 'tests', 'io_util_test') | ||
) | ||
|
||
fn testsuite_begin() { | ||
eprintln('testsuite_begin, tfolder = $tfolder') | ||
os.rmdir_all(tfolder) | ||
assert !os.is_dir(tfolder) | ||
os.mkdir_all(tfolder) | ||
os.chdir(tfolder) | ||
assert os.is_dir(tfolder) | ||
} | ||
|
||
fn testsuite_end() { | ||
os.chdir(os.wd_at_startup) | ||
os.rmdir_all(tfolder) | ||
assert !os.is_dir(tfolder) | ||
// eprintln('testsuite_end , tfolder = $tfolder removed.') | ||
} | ||
|
||
fn test_temp_file() { | ||
// Test defaults | ||
mut f, mut path := util.temp_file({}) or { | ||
assert false | ||
return | ||
} | ||
mut prev_path := path | ||
defer { | ||
f.close() | ||
} | ||
assert os.is_file(path) | ||
assert f.is_opened | ||
// Test pattern | ||
f.close() | ||
f, path = util.temp_file({ | ||
pattern: 'some_*_test.file' | ||
}) or { | ||
assert false | ||
return | ||
} | ||
assert path != prev_path | ||
assert os.is_file(path) | ||
assert f.is_opened | ||
mut filename := os.file_name(path) | ||
assert filename.contains('_test.file') | ||
// Check for 9 digits where the wildcard is placed in the pattern | ||
for i, c in filename { | ||
if i > 4 && i <= 4 + 9 { | ||
assert c.is_digit() | ||
} | ||
} | ||
// Test custom path | ||
prev_path = path | ||
f.close() | ||
f, path = util.temp_file({ | ||
path: tfolder | ||
}) or { | ||
assert false | ||
return | ||
} | ||
assert path != prev_path | ||
assert os.is_file(path) | ||
assert path.contains(tfolder) | ||
assert f.is_opened | ||
filename = os.file_name(path) | ||
for c in filename { | ||
assert c.is_digit() | ||
} | ||
} | ||
|
||
fn test_temp_dir() { | ||
// Test defaults | ||
mut path := util.temp_dir({}) or { | ||
assert false | ||
return | ||
} | ||
assert os.is_dir(path) | ||
mut writable := os.is_writable_folder(path) or { | ||
assert false | ||
return | ||
} | ||
assert writable | ||
mut prev_path := path | ||
// Test pattern | ||
path = util.temp_dir({ | ||
pattern: 'some_*_test_dir' | ||
}) or { | ||
assert false | ||
return | ||
} | ||
assert path != prev_path | ||
assert os.is_dir(path) | ||
mut filename := os.file_name(path) | ||
assert filename.contains('_test_dir') | ||
// Check for 9 digits where the wildcard is placed in the pattern | ||
for i, c in filename { | ||
if i > 4 && i <= 4 + 9 { | ||
assert c.is_digit() | ||
} | ||
} | ||
// Test custom path | ||
prev_path = path | ||
path = util.temp_dir({ | ||
path: tfolder | ||
}) or { | ||
assert false | ||
return | ||
} | ||
assert path != prev_path | ||
assert os.is_dir(path) | ||
writable = os.is_writable_folder(path) or { | ||
assert false | ||
return | ||
} | ||
assert writable | ||
assert path.contains(tfolder) | ||
filename = os.file_name(path) | ||
for c in filename { | ||
assert c.is_digit() | ||
} | ||
} |
The
= ''
is not needed :-)