Class: OpenDal::IO

Inherits:
Object
  • Object
show all
Defined in:
src/io.rs,
lib/opendal_ruby/io.rb

Overview

OpenDal::IO is similar to Ruby's IO and StringIO for accessing files.

You can't create an instance of OpenDal::IO except using Operator#open.

Constraints:

  • Only available for reading and writing
  • Writing doesn't support seek.

Instance Method Summary collapse

Instance Method Details

#binmodenil

Enables binary mode for the stream.

Returns:

  • (nil)

Raises:

  • (IOError)

    when operate on a closed stream



136
137
138
139
140
141
142
143
# File 'src/io.rs', line 136

fn binary_mode(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
    let mut handle = rb_self.0.borrow_mut();
    if let FileState::Closed = handle.state {
        return Err(Error::new(ruby.exception_io_error(), "closed stream"));
    }
    handle.fmode = FMode::new(handle.fmode.bits() | FMode::BINARY_MODE);
    Ok(())
}

#binmode?Boolean

Returns if the stream is on binary mode.

Returns:

  • (Boolean)

Raises:

  • (IOError)

    when operate on a closed stream



150
151
152
153
154
155
156
# File 'src/io.rs', line 150

fn is_binary_mode(ruby: &Ruby, rb_self: &Self) -> Result<bool, Error> {
    let handle = rb_self.0.borrow();
    if let FileState::Closed = handle.state {
        return Err(Error::new(ruby.exception_io_error(), "closed stream"));
    }
    Ok(handle.fmode.contains(FMode::BINARY_MODE))
}

#closenil

Close streams.

Returns:

  • (nil)


162
163
164
165
166
167
168
169
# File 'src/io.rs', line 162

fn close(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
    let mut handle = rb_self.0.borrow_mut();
    if let FileState::Writer(writer) = &mut handle.state {
        writer.close().map_err(|err| format_io_error(ruby, err))?;
    }
    handle.state = FileState::Closed;
    Ok(())
}

#close_readnil

Closes the read stream.

Returns:

  • (nil)


175
176
177
178
179
180
181
# File 'src/io.rs', line 175

fn close_read(&self) -> Result<(), Error> {
    let mut handle = self.0.borrow_mut();
    if let FileState::Reader(_) = &handle.state {
        handle.state = FileState::Closed;
    }
    Ok(())
}

#close_writenil

Closes the write stream.

Returns:

  • (nil)


187
188
189
190
191
192
193
194
# File 'src/io.rs', line 187

fn close_write(ruby: &Ruby, rb_self: &Self) -> Result<(), Error> {
    let mut handle = rb_self.0.borrow_mut();
    if let FileState::Writer(writer) = &mut handle.state {
        writer.close().map_err(|err| format_io_error(ruby, err))?;
        handle.state = FileState::Closed;
    }
    Ok(())
}

#closed?Boolean

Returns if streams are closed.

Returns:

  • (Boolean)


200
201
202
203
# File 'src/io.rs', line 200

fn is_closed(&self) -> Result<bool, Error> {
    let handle = self.0.borrow();
    Ok(matches!(handle.state, FileState::Closed))
}

#closed_read?Boolean

Returns if the read stream is closed.

Returns:

  • (Boolean)


209
210
211
212
# File 'src/io.rs', line 209

fn is_closed_read(&self) -> Result<bool, Error> {
    let handle = self.0.borrow();
    Ok(!matches!(handle.state, FileState::Reader(_)))
}

#closed_write?Boolean

Returns if the write stream is closed.

Returns:

  • (Boolean)


218
219
220
221
222
# File 'src/io.rs', line 218

fn is_closed_write(&self) -> Result<bool, Error> {
        let handle = self.0.borrow();
        Ok(!matches!(handle.state, FileState::Writer(_)))
    }
}

#eofBoolean Also known as: eof?

Checks if the stream is at the end of the file.

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/opendal_ruby/io.rb', line 52

def eof
  position = tell
  seek(0, ::IO::SEEK_END)
  tell == position
end

#lengthInteger Also known as: size

Returns the total length of the stream.

Returns:

  • (Integer)


62
63
64
65
66
# File 'lib/opendal_ruby/io.rb', line 62

def length
  current_position = tell
  seek(0, ::IO::SEEK_END)
  tell.tap { self.pos = current_position }
end

#pos=(new_position) ⇒ Object

Sets the file position to new_position.

Parameters:

  • new_position (Integer)


44
45
46
# File 'lib/opendal_ruby/io.rb', line 44

def pos=(new_position)
  seek(new_position, ::IO::SEEK_SET)
end

#readlineString

Reads a single line from the stream.

Returns:

  • (String)


289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'src/io.rs', line 289

fn readline(ruby: &Ruby, rb_self: &Self) -> Result<String, Error> {
    let mut handle = rb_self.0.borrow_mut();

    if let FileState::Reader(reader) = &mut handle.state {
        let mut buffer = String::new();
        let size = reader
            .read_line(&mut buffer)
            .map_err(|err| format_io_error(ruby, err))?;
        if size == 0 {
            return Err(Error::new(
                ruby.exception_eof_error(),
                "end of file reached",
            ));
        }

        Ok(buffer)
    } else {
        Err(Error::new(
            ruby.exception_runtime_error(),
            "I/O operation failed for reading on write-only file.",
        ))
    }
}

#readlinesArray<String>

Reads all lines from the stream into an array.

Returns:

  • (Array<String>)

Raises:

  • (EOFError)

    when the end of the file is reached.



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/opendal_ruby/io.rb', line 25

def readlines
  results = []

  loop do
    results << readline
  rescue EOFError
    break
  end

  results
end

#rewindObject

Rewinds the stream to the beginning.



38
39
40
# File 'lib/opendal_ruby/io.rb', line 38

def rewind
  seek(0, ::IO::SEEK_SET)
end

#seek(offset, whence) ⇒ Integer

Moves the file position based on the offset and whence.

Parameters:

  • offset (Integer)

    The position offset.

  • whence (Integer)

    The reference point:

    • 0 = IO:SEEK_SET (Start)
    • 1 = IO:SEEK_CUR (Current position)
    • 2 = IO:SEEK_END (From the end)

Returns:

  • (Integer)

    always 0 if the seek operation is successful



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'src/io.rs', line 347

fn seek(ruby: &Ruby, rb_self: &Self, offset: i64, whence: u8) -> Result<u8, Error> {
    let mut handle = rb_self.0.borrow_mut();

    if let FileState::Reader(reader) = &mut handle.state {
        // Calculate the new position first
        let new_reader_position = match whence {
            0 => {
                // SEEK_SET - absolute position
                if offset < 0 {
                    return Err(Error::new(
                        ruby.exception_runtime_error(),
                        "Cannot seek to negative reader_position.",
                    ));
                }
                offset as u64
            }
            1 => {
                // SEEK_CUR - relative to current position
                let position = reader
                    .stream_position()
                    .map_err(|err| format_io_error(ruby, err))?;
                if offset < 0 && (-offset as u64) > position {
                    return Err(Error::new(
                        ruby.exception_runtime_error(),
                        "Cannot seek before start of stream.",
                    ));
                }
                (position as i64 + offset) as u64
            }
            2 => {
                // SEEK_END
                let end_pos = reader
                    .seek(SeekFrom::End(0))
                    .map_err(|err| format_io_error(ruby, err))?;
                if offset < 0 && (-offset as u64) > end_pos {
                    return Err(Error::new(
                        ruby.exception_runtime_error(),
                        "Cannot seek before start of stream.",
                    ));
                }
                (end_pos as i64 + offset) as u64
            }
            _ => return Err(Error::new(ruby.exception_arg_error(), "invalid whence")),
        };

        let _ = reader.seek(std::io::SeekFrom::Start(new_reader_position));
    } else {
        return Err(Error::new(
            ruby.exception_runtime_error(),
            "Cannot seek from end on write-only stream.",
        ));
    }

    Ok(0)
}

#tellInteger Also known as: pos

Returns the current reader_position of the file pointer in the stream.

Returns:

  • (Integer)

    the current reader_position in bytes

Raises:

  • (IOError)

    when cannot operate on the operation mode



408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'src/io.rs', line 408

fn tell(ruby: &Ruby, rb_self: &Self) -> Result<u64, Error> {
    let mut handle = rb_self.0.borrow_mut();

    match &mut handle.state {
        FileState::Reader(reader) => Ok(reader
            .stream_position()
            .map_err(|err| format_io_error(ruby, err))?),
        FileState::Writer(_) => Err(Error::new(
            ruby.exception_runtime_error(),
            "I/O operation failed for reading on write only file.",
        )),
        FileState::Closed => Err(Error::new(
            ruby.exception_runtime_error(),
            "I/O operation failed for tell on closed stream.",
        )),
    }
}

#write(buffer) ⇒ Integer

Writes data to the stream.

Parameters:

  • buffer (String)

Returns:

  • (Integer)

    the written byte size



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'src/io.rs', line 318

fn write(ruby: &Ruby, rb_self: &Self, bs: String) -> Result<usize, Error> {
        let mut handle = rb_self.0.borrow_mut();

        if let FileState::Writer(writer) = &mut handle.state {
            let bytes_written = bs.len();
            writer
                .write_all(bs.as_bytes())
                .map_err(|err| format_io_error(ruby, err))?;
            Ok(bytes_written)
        } else {
            Err(Error::new(
                ruby.exception_runtime_error(),
                "I/O operation failed for writing on read-only file.",
            ))
        }
    }
}