diff options
Diffstat (limited to 'v_windows/v/old/vlib/io')
-rw-r--r-- | v_windows/v/old/vlib/io/buffered_reader.v | 145 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/custom_string_reading_test.v | 57 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/io.v | 16 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/io_cp_test.v | 13 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/io_test.v | 41 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/multi_writer.v | 33 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/multi_writer_test.v | 66 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/os_file_reader_test.v | 29 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/reader.v | 76 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/reader_test.v | 130 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/readerwriter.v | 34 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/util/util.v | 104 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/util/util_test.v | 127 | ||||
-rw-r--r-- | v_windows/v/old/vlib/io/writer.v | 12 |
14 files changed, 883 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/io/buffered_reader.v b/v_windows/v/old/vlib/io/buffered_reader.v new file mode 100644 index 0000000..f336e2d --- /dev/null +++ b/v_windows/v/old/vlib/io/buffered_reader.v @@ -0,0 +1,145 @@ +module io + +// BufferedReader provides a buffered interface for a reader +struct BufferedReader { +mut: + reader Reader + buf []byte + offset int // current offset in the buffer + len int + fails int // how many times fill_buffer has read 0 bytes in a row + mfails int // maximum fails, after which we can assume that the stream has ended +pub mut: + end_of_stream bool // whether we reached the end of the upstream reader +} + +// BufferedReaderConfig are options that can be given to a reader +pub struct BufferedReaderConfig { + reader Reader + cap int = 128 * 1024 // large for fast reading of big(ish) files + retries int = 2 // how many times to retry before assuming the stream ended +} + +// new_buffered_reader creates new BufferedReader +pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader { + if o.cap <= 0 { + panic('new_buffered_reader should be called with a positive `cap`') + } + // create + r := &BufferedReader{ + reader: o.reader + buf: []byte{len: o.cap, cap: o.cap} + offset: 0 + mfails: o.retries + } + return r +} + +// read fufills the Reader interface +pub fn (mut r BufferedReader) read(mut buf []byte) ?int { + if r.end_of_stream { + return none + } + // read data out of the buffer if we dont have any + if r.needs_fill() { + if !r.fill_buffer() { + // end of stream + return none + } + } + read := copy(buf, r.buf[r.offset..r.len]) + if read == 0 { + return none + } + r.offset += read + return read +} + +pub fn (mut r BufferedReader) free() { + unsafe { + r.buf.free() + } +} + +// fill_buffer attempts to refill the internal buffer +// and returns whether it got any data +fn (mut r BufferedReader) fill_buffer() bool { + if r.end_of_stream { + // we know we have already reached the end of stream + // so return early + return true + } + r.offset = 0 + r.len = 0 + r.len = r.reader.read(mut r.buf) or { + // end of stream was reached + r.end_of_stream = true + return false + } + if r.len == 0 { + r.fails++ + } else { + r.fails = 0 + } + if r.fails >= r.mfails { + // When reading 0 bytes several times in a row, assume the stream has ended. + // This prevents infinite loops ¯\_(ツ)_/¯ ... + r.end_of_stream = true + return false + } + // we got some data + return true +} + +// needs_fill returns whether the buffer needs refilling +fn (r BufferedReader) needs_fill() bool { + return r.offset >= r.len +} + +// end_of_stream returns whether the end of the stream was reached +pub fn (r BufferedReader) end_of_stream() bool { + return r.end_of_stream +} + +// read_line attempts to read a line from the buffered reader +// it will read until it finds a new line character (\n) or +// the end of stream +pub fn (mut r BufferedReader) read_line() ?string { + if r.end_of_stream { + return none + } + mut line := []byte{} + for { + if r.needs_fill() { + // go fetch some new data + if !r.fill_buffer() { + // We are at the end of the stream + if line.len == 0 { + // we had nothing so return nothing + return none + } + return line.bytestr() + } + } + // try and find a newline character + mut i := r.offset + for ; i < r.len; i++ { + c := r.buf[i] + if c == `\n` { + // great, we hit something + // do some checking for whether we hit \r\n or just \n + if i != 0 && r.buf[i - 1] == `\r` { + x := i - 1 + line << r.buf[r.offset..x] + } else { + line << r.buf[r.offset..i] + } + r.offset = i + 1 + return line.bytestr() + } + } + line << r.buf[r.offset..i] + r.offset = i + } + return none +} diff --git a/v_windows/v/old/vlib/io/custom_string_reading_test.v b/v_windows/v/old/vlib/io/custom_string_reading_test.v new file mode 100644 index 0000000..76e3307 --- /dev/null +++ b/v_windows/v/old/vlib/io/custom_string_reading_test.v @@ -0,0 +1,57 @@ +import io + +struct StringReader { + text string +mut: + place int +} + +fn imin(a int, b int) int { + return if a < b { a } else { b } +} + +fn (mut s StringReader) read(mut buf []byte) ?int { + $if debug { + eprintln('>>>> StringReader.read output buf.len: $buf.len') + } + if s.place > s.text.len + 1 { + return none + } + mut howmany := imin(buf.len, s.text.len - s.place) + xxx := s.text[s.place..s.place + howmany].bytes() + read := copy(buf, xxx) + s.place += read + return read +} + +fn read_from_string(text string, capacity int) []byte { + mut str := StringReader{ + text: text + } + mut stream := io.new_buffered_reader(reader: str, cap: capacity) + // + mut buf := []byte{len: 1} + mut res := []byte{} + mut i := 0 + for { + z := stream.read(mut buf) or { break } + res << buf + $if debug { + println('capacity: $capacity, i: $i, buf: $buf | z: $z') + } + i++ + } + return res +} + +pub fn test_reading_from_a_string() { + for capacity in 1 .. 1000 { + assert read_from_string('a', capacity) == [byte(`a`)] + assert read_from_string('ab', capacity) == [byte(`a`), `b`] + assert read_from_string('abc', capacity) == [byte(`a`), `b`, `c`] + assert read_from_string('abcde', capacity) == [byte(`a`), `b`, `c`, `d`, `e`] + large_string_bytes := []byte{len: 1000, init: `x`} + large_string := large_string_bytes.bytestr() + assert read_from_string(large_string, capacity) == large_string_bytes + } +} diff --git a/v_windows/v/old/vlib/io/io.v b/v_windows/v/old/vlib/io/io.v new file mode 100644 index 0000000..c07e074 --- /dev/null +++ b/v_windows/v/old/vlib/io/io.v @@ -0,0 +1,16 @@ +module io + +const ( + buf_max_len = 1024 +) + +pub fn cp(src Reader, mut dst Writer) ? { + mut buf := []byte{len: io.buf_max_len} + for { + len := src.read(mut buf) or { break } + dst.write(buf[..len]) or { return err } + } + unsafe { + buf.free() + } +} diff --git a/v_windows/v/old/vlib/io/io_cp_test.v b/v_windows/v/old/vlib/io/io_cp_test.v new file mode 100644 index 0000000..21c743a --- /dev/null +++ b/v_windows/v/old/vlib/io/io_cp_test.v @@ -0,0 +1,13 @@ +import io +import os + +fn test_cp() ? { + mut f := os.open(@FILE) or { panic(err) } + defer { + f.close() + } + mut r := io.new_buffered_reader(reader: f) + mut stdout := os.stdout() + io.cp(r, mut stdout) ? + assert true +} diff --git a/v_windows/v/old/vlib/io/io_test.v b/v_windows/v/old/vlib/io/io_test.v new file mode 100644 index 0000000..fca17c2 --- /dev/null +++ b/v_windows/v/old/vlib/io/io_test.v @@ -0,0 +1,41 @@ +import io + +struct Buf { +pub: + bytes []byte +mut: + i int +} + +struct Writ { +pub mut: + bytes []byte +} + +fn (mut b Buf) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn (mut w Writ) write(buf []byte) ?int { + if buf.len <= 0 { + return none + } + w.bytes << buf + return buf.len +} + +fn test_copy() { + src := Buf{ + bytes: 'abcdefghij'.repeat(10).bytes() + } + mut dst := Writ{ + bytes: []byte{} + } + io.cp(src, mut dst) or { assert false } + assert dst.bytes == src.bytes +} diff --git a/v_windows/v/old/vlib/io/multi_writer.v b/v_windows/v/old/vlib/io/multi_writer.v new file mode 100644 index 0000000..837ea30 --- /dev/null +++ b/v_windows/v/old/vlib/io/multi_writer.v @@ -0,0 +1,33 @@ +module io + +// new_multi_writer returns a Writer that writes to all writers. The write +// function of the returned Writer writes to all writers of the MultiWriter, +// returns the length of bytes written, and if any writer fails to write the +// full length an error is returned and writing to other writers stops, and if +// any writer returns an error the error is returned immediately and writing to +// other writers stops. +pub fn new_multi_writer(writers ...Writer) Writer { + return &MultiWriter{ + writers: writers + } +} + +// MultiWriter writes to all its writers. +pub struct MultiWriter { +pub mut: + writers []Writer +} + +// write writes to all writers of the MultiWriter. Returns the length of bytes +// written. If any writer fails to write the full length an error is returned +// and writing to other writers stops. If any writer returns an error the error +// is returned immediately and writing to other writers stops. +pub fn (mut m MultiWriter) write(buf []byte) ?int { + for mut w in m.writers { + n := w.write(buf) ? + if n != buf.len { + return error('io: incomplete write to writer of MultiWriter') + } + } + return buf.len +} diff --git a/v_windows/v/old/vlib/io/multi_writer_test.v b/v_windows/v/old/vlib/io/multi_writer_test.v new file mode 100644 index 0000000..2e317e8 --- /dev/null +++ b/v_windows/v/old/vlib/io/multi_writer_test.v @@ -0,0 +1,66 @@ +module io + +fn test_multi_writer_write_successful() { + w0 := TestWriter{} + w1 := TestWriter{} + mut mw := new_multi_writer(w0, w1) + n := mw.write('0123456789'.bytes()) or { + assert false + return + } + assert n == 10 + assert w0.bytes == '0123456789'.bytes() + assert w1.bytes == '0123456789'.bytes() +} + +fn test_multi_writer_write_incomplete() { + w0 := TestWriter{} + w1 := TestIncompleteWriter{} + mut mw := new_multi_writer(w0, w1) + n := mw.write('0123456789'.bytes()) or { + assert w0.bytes == '0123456789'.bytes() + assert w1.bytes == '012345678'.bytes() + return + } + assert false +} + +fn test_multi_writer_write_error() { + w0 := TestWriter{} + w1 := TestErrorWriter{} + w2 := TestWriter{} + mut mw := new_multi_writer(w0, w1, w2) + n := mw.write('0123456789'.bytes()) or { + assert w0.bytes == '0123456789'.bytes() + assert w2.bytes == [] + return + } + assert false +} + +struct TestWriter { +pub mut: + bytes []byte +} + +fn (mut w TestWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} + +struct TestIncompleteWriter { +pub mut: + bytes []byte +} + +fn (mut w TestIncompleteWriter) write(buf []byte) ?int { + b := buf[..buf.len - 1] + w.bytes << b + return b.len +} + +struct TestErrorWriter {} + +fn (mut w TestErrorWriter) write(buf []byte) ?int { + return error('error writer errored') +} diff --git a/v_windows/v/old/vlib/io/os_file_reader_test.v b/v_windows/v/old/vlib/io/os_file_reader_test.v new file mode 100644 index 0000000..1ed3a1f --- /dev/null +++ b/v_windows/v/old/vlib/io/os_file_reader_test.v @@ -0,0 +1,29 @@ +import os +import io + +fn read_file(file string, cap int) []string { + mut lines := []string{} + mut f := os.open(file) or { panic(err) } + defer { + f.close() + } + mut r := io.new_buffered_reader(reader: f, cap: cap) + for { + l := r.read_line() or { break } + lines << l + // println('Line: $l') + } + assert lines.len > 0 + assert r.end_of_stream == true + println('------------------------------------------------ cap: ${cap:6}; read: ${lines.len:3} lines') + return lines +} + +fn test_file_reader() { + for cap := 1; cap <= 10000; cap += 256 { + lines := read_file(@FILE, cap) + assert lines.last() == '// my last line' + } +} + +// my last line diff --git a/v_windows/v/old/vlib/io/reader.v b/v_windows/v/old/vlib/io/reader.v new file mode 100644 index 0000000..b7377b0 --- /dev/null +++ b/v_windows/v/old/vlib/io/reader.v @@ -0,0 +1,76 @@ +module io + +// Reader represents a stream of data that can be read +pub interface Reader { + // read reads up to buf.len bytes and places + // them into buf. + // A type that implements this should return + // `none` on end of stream (EOF) instead of just returning 0 + read(mut buf []byte) ?int +} + +// make_reader is a temp that converts a type to a reader +// (e.g. for use in struct initialisation) +// (this shouldnt need to be a thing but until coercion gets made better +// it is required) +[deprecated: 'use just `x` instead of `io.make_reader(x)`. Interfaces are now checked against all types.'] +[deprecated_after: '2021-05-27'] +pub fn make_reader(r Reader) Reader { + return r +} + +const ( + read_all_len = 10 * 1024 + read_all_grow_len = 1024 +) + +// ReadAllConfig allows options to be passed for the behaviour +// of read_all +pub struct ReadAllConfig { + reader Reader + read_to_end_of_stream bool +} + +// read_all reads all bytes from a reader until either a 0 length read +// or if read_to_end_of_stream is true then the end of the stream (`none`) +pub fn read_all(config ReadAllConfig) ?[]byte { + r := config.reader + read_till_eof := config.read_to_end_of_stream + + mut b := []byte{len: io.read_all_len} + mut read := 0 + for { + new_read := r.read(mut b[read..]) or { break } + read += new_read + if !read_till_eof && read == 0 { + break + } + if b.len == read { + unsafe { b.grow_len(io.read_all_grow_len) } + } + } + return b[..read] +} + +// read_any reads any available bytes from a reader +// (until the reader returns a read of 0 length) +pub fn read_any(r Reader) ?[]byte { + mut b := []byte{len: io.read_all_len} + mut read := 0 + for { + new_read := r.read(mut b[read..]) or { break } + read += new_read + if new_read == 0 { + break + } + if b.len == read { + unsafe { b.grow_len(io.read_all_grow_len) } + } + } + return b[..read] +} + +// RandomReader represents a stream of data that can be read from at a random location +interface RandomReader { + read_from(pos u64, mut buf []byte) ?int +} diff --git a/v_windows/v/old/vlib/io/reader_test.v b/v_windows/v/old/vlib/io/reader_test.v new file mode 100644 index 0000000..c7a2265 --- /dev/null +++ b/v_windows/v/old/vlib/io/reader_test.v @@ -0,0 +1,130 @@ +module io + +struct Buf { +pub: + bytes []byte +mut: + i int +} + +fn (mut b Buf) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn test_read_all() { + buf := Buf{ + bytes: '123'.repeat(10).bytes() + } + res := read_all(reader: buf) or { + assert false + ''.bytes() + } + assert res == '123'.repeat(10).bytes() +} + +fn test_read_all_huge() { + buf := Buf{ + bytes: '123'.repeat(100000).bytes() + } + res := read_all(reader: buf) or { + assert false + ''.bytes() + } + assert res == '123'.repeat(100000).bytes() +} + +struct StringReader { + text string +mut: + place int +} + +fn (mut s StringReader) read(mut buf []byte) ?int { + if s.place >= s.text.len { + return none + } + read := copy(buf, s.text[s.place..].bytes()) + s.place += read + return read +} + +const ( + newline_count = 100000 +) + +fn test_stringreader() { + text := '12345\n'.repeat(io.newline_count) + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + for i := 0; true; i++ { + if _ := r.read_line() { + } else { + assert i == io.newline_count + break + } + } + if _ := r.read_line() { + assert false + } + leftover := read_all(reader: r) or { + assert false + panic('bad') + } + if leftover.len > 0 { + assert false + } +} + +fn test_stringreader2() { + text := '12345\r\n'.repeat(io.newline_count) + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + for i := 0; true; i++ { + if _ := r.read_line() { + } else { + assert i == io.newline_count + break + } + } + if _ := r.read_line() { + assert false + } + leftover := read_all(reader: r) or { + assert false + panic('bad') + } + if leftover.len > 0 { + assert false + } +} + +fn test_leftover() { + text := 'This is a test\r\nNice try!' + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + _ := r.read_line() or { + assert false + panic('bad') + } + line2 := r.read_line() or { + assert false + panic('bad') + } + assert line2 == 'Nice try!' + if _ := r.read_line() { + assert false + panic('bad') + } + assert r.end_of_stream() +} diff --git a/v_windows/v/old/vlib/io/readerwriter.v b/v_windows/v/old/vlib/io/readerwriter.v new file mode 100644 index 0000000..ba44172 --- /dev/null +++ b/v_windows/v/old/vlib/io/readerwriter.v @@ -0,0 +1,34 @@ +module io + +// ReaderWriter represents a stream that can be read from and wrote to +pub interface ReaderWriter { + // from Reader + read(mut buf []byte) ?int + // from Writer + write(buf []byte) ?int +} + +// ReaderWriterImpl is a ReaderWriter that can be made from +// a seperate reader and writer (see fn make_readerwriter) +struct ReaderWriterImpl { + r Reader +mut: + w Writer +} + +pub fn (mut r ReaderWriterImpl) read(mut buf []byte) ?int { + return r.r.read(mut buf) +} + +pub fn (mut r ReaderWriterImpl) write(buf []byte) ?int { + return r.w.write(buf) +} + +// make_readerwriter takes a rstream and a wstream and makes +// an rwstream with them +pub fn make_readerwriter(r Reader, w Writer) ReaderWriterImpl { + return ReaderWriterImpl{ + r: r + w: w + } +} diff --git a/v_windows/v/old/vlib/io/util/util.v b/v_windows/v/old/vlib/io/util/util.v new file mode 100644 index 0000000..6f0d93f --- /dev/null +++ b/v_windows/v/old/vlib/io/util/util.v @@ -0,0 +1,104 @@ +module util + +import os +import rand +import rand.seed as rseed + +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() + prefix, suffix := prefix_and_suffix(tfo.pattern) or { return error(@FN + ' ' + err.msg) } + for retry := 0; retry < util.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(rseed.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 ($util.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() + prefix, suffix := prefix_and_suffix(tdo.pattern) or { return error(@FN + ' ' + err.msg) } + for retry := 0; retry < util.retries; retry++ { + path := os.join_path(d, prefix + random_number(mut rng) + suffix) + os.mkdir_all(path) or { + rng.seed(rseed.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 ($util.retries) exhausted. Please ensure write permissions.') +} + +// * Utility functions +fn random_number(mut rng rand.PRNG) 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 +} diff --git a/v_windows/v/old/vlib/io/util/util_test.v b/v_windows/v/old/vlib/io/util/util_test.v new file mode 100644 index 0000000..1072cb5 --- /dev/null +++ b/v_windows/v/old/vlib/io/util/util_test.v @@ -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) or {} + assert !os.is_dir(tfolder) + os.mkdir_all(tfolder) or { panic(err) } + os.chdir(tfolder) + assert os.is_dir(tfolder) +} + +fn testsuite_end() { + os.chdir(os.wd_at_startup) + os.rmdir_all(tfolder) or {} + 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() + } +} diff --git a/v_windows/v/old/vlib/io/writer.v b/v_windows/v/old/vlib/io/writer.v new file mode 100644 index 0000000..322dd45 --- /dev/null +++ b/v_windows/v/old/vlib/io/writer.v @@ -0,0 +1,12 @@ +module io + +// Writer represents a stream of data that can be wrote to +pub interface Writer { + write(buf []byte) ?int +} + +// RandomWriter represents a stream of data that can be wrote to +// at a random pos +pub interface RandomWriter { + write_to(pos u64, buf []byte) ?int +} |