-
-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement std::io traits for FileAccess #446
Comments
There is a more general question, regarding how far gdext should go and make engine APIs rusty. We would end up with conflicting APIs, e.g. fn FileAccess::seek(&mut self, position: i32);
fn FileAccess::seek_end(&mut self, position: i32);
// vs.
fn Seek::seek(&mut self, pos: SeekFrom) -> Result<u64>; Even if we can resolve naming conflicts, modifying Can you elaborate what motivated your use case, beyond "it's possible"? 🙂 |
This came up in a discussion on Discord on getting data for a SQLite table into a relevant library where we found that getting the bytes for a files to require some massaging. However, I think that case landed on finding a better solution, and I dont personally have an application for it. So firmly in the realm of its possible and sounds reasonable, but doesnt solve an immediate problem. |
I think that makes more sense, and we could perhaps even decouple the wrappers from the underlying Godot things - have something that just works:tm: by using the underlying Godot APIs for it (and, if you need full flexibility, then just call them manually). In this example, we'd have some Filesystem wrapper or something which uses FileAccess and similar things under the hood. (Of course, we could be kinda limited in the amount of decoupling possible since e.g. it could be necessary to place a node in the scene tree or something like that. But, in principle, it could be a nice path to follow.) |
So I've implemented With some pointers of how this functionality could be done safer, and more useful and how it should be implemented into |
@Bromeon This is something I came upon after cleaning up the implementation mentioned above. Any pointers, also regarding the style in which it could be implemented in |
@StatisMike thanks for the initial implementation! Some feedback: the current implementation is quite a thin and transparent wrapper around
Feel free to open a PR once those changes are applied 🙂 |
@Bromeon Struct name was only prototyping, will refer to the struct below as My rationale behind (at very least) I don't really think that there is any option to update the wrapper I could potentially expose all of AD the EDIT: |
Oh, I missed that. This is actually quite a pitfall: users will certainly forget this, naming it Instead of storing the position, would it be possible to sync it with Godot's
I was thinking of locking it down completely, too -- however, the issue is that you lose all interoperability with Godot APIs. Not sure if there are many cases where a Maybe a hybrid approach is possible: have a field
I wouldn't worry about performance here. We're doing disk access, sometimes even with encryption or compression; a few indirect function calls don't fall into weight. If they do, users are free to always use
I think this defeats the point of having an "idiomatic Rust" wrapper. If we can make user's life easier while we're at it, let's do it 🙂 (Btw, I don't want to impose extra work on you here -- it's totally fine if the initial PR comes without error checking. I'm just stating where I could imagine the API heading) |
@Bromeon No worries, I like to discuss Rust and happy to provide a good quality code to the community :)
This calms me down a little, wrapper methods will then always check for errors and will actually produce AD the exposing inner, I think that wrapping most methods will be actually for the best and will actually make the possibility of needing an pub struct GFile {
// The FileAccess is kept as private
fa: Gd<FileAccess>,
pos: u64,
buffer: Vec<u8>,
buffer_size: usize
}
impl GFile {
// New GFile is constructed by calling GFile::open_* methods
pub fn open(path: GodotString, flags: ModeFlags) -> std::io::Result<Self> { ... }
pub fn open_compressed(path: GodotString, flags: ModeFlags, compression_mode: CompressionMode) -> std::io::Result<Self> { ... }
pub fn open_encrypted(path: GodotString, flags: ModeFlags, key: PackedByteArray) -> std::io::Result<Self> { ... }
pub fn open_encrypted_with_pass(path: GodotString, flags: ModeFlags, pass: GodotString) -> std::io::Result<Self> { ... }
// Obvious cursor moves can be tracked on the Rust side without needing the call to `self.fa.get_position()`
pub fn get_8(&mut self) -> IoResult<u8> {
let val = self.fa.get_8();
self.check_error()?;
self.pos += 1;
Ok(val)
}
// User can retrieve the `Gd<FileAccess>` from `GFile` by destroying the wrapper
pub fn into_inner(self) -> Gd<FileAccess> {
self.fa
}
}
// But can also easily create the `GFile` from the `Gd<FileAccess>` anytime they want:
impl From<Gd<FileAccess>> for GFile {
fn from(value: Gd<FileAccess>) -> Self {
let pos = value.get_position();
Self {
fa: value,
pos,
buffer: Vec::new(),
buffer_size: Self::DEFAULT_BUFFER_SIZE
}
}
} If you feel that it is a deal-breaker, I will call Edit: Actually, disregard my musing about the position. Actually the tracking of the position is useful only in handful of methods and it's impact should be negligible Edit: I'm thinking about skipping some wrapping methods:
|
Yes, definitely skip those methods which are available in About reading/writing integers of various sizes, should we provide this on the wrapper types? I guess yes? I would also rename them slightly, starting with pub fn read_u8(&mut self) -> IoResult<u8> {
self.read_internal(FileAccess::get_8) // maybe closure needed
}
fn read_internal<T, F>(&mut self, read_fn: F) -> IoResult<T>
where
F: Fn(&FileAccess) -> T,
{
let val = read_fn(&*self.inner);
self.check_error()?;
self.pos += std::mem::size_of::<T>();
Ok(val)
} (probably needs some tweaks, but that's the general idea). Feel free to open a pull request, it's easier to comment on concrete code lines than having a discussion here, and the code being referred to is also not lost over time. There are two ways to deal with wrapper vs. inner:
|
I've actually removed the Another strange thing I've seen is that the |
Yeah, Godot's API is full of wrong const-correctness, unfortunately. Something we can fix in Rust though 🙂
To me it seemed like the |
I've opened the WIP PR with current work, most of the documentation is still missing. I also plan to create
Renaming the functions to I think we also should mark the reexports through the
So I guess I should make the |
Good point, maybe
Why? From the user perspective, it doesn't matter. They just want to read/write files.
Yes, would be nice! 🙂 |
API users interested in File IO in the res:// and user:// file systems need to work with Godot's
FileAccess
API. gdext should provide a Rusty API for this by implementing some subset ofstd::io::{BufRead, IsTerminal, Read, Seek, Write}
forgodot::engine::FileAccess
.The text was updated successfully, but these errors were encountered: