io: introduce a go-like io.util module (#6323)
							parent
							
								
									26971da510
								
							
						
					
					
						commit
						1c5b9db63f
					
				|  | @ -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 = '' | ||||
| } | ||||
| 
 | ||||
| // 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++ { | ||||
| 		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 | ||||
| } | ||||
|  | @ -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() | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue