-
Notifications
You must be signed in to change notification settings - Fork 111
Description
Working with async I/O is currently tricky as ResourceReader is not capable of providing a reader asynchronously. I've needed async loading in a project before, so I think adding it as a functionality would be a worthwhile change.
I've implemented a solution here. Broadly speaking, I made the following changes:
-
Switched from
xml-rstoquick-xml, which provides async reading. This was also proposed in Consider switching from xml-rs to quick-xml #137. A noticeable difference between these crates is thatxml-rsexpects aReadwhilequick-xmlexpects aBufRead. Accordingly, I've changed theReadbound onResourceReader::ResourcetoBufRead. This is technically a breaking change, but is worth it in my opinion: most people probably only useFilesystemResourceReaderorstd::io::Cursor, which would both still work. Otherwise, wrapping a reader instd::io::BufReadershould not be a difficult change (and of course, we're before 1.0). -
Added an
AsyncResourceReadertrait, mirroringResourceReader, that asynchronously provides atokio::io::AsyncBufRead. -
Removed the
ResourceReaderbound onLoaderand moved it to the loading functions to which I added async counterparts. The alternative would be to create a separateAsyncLoaderstruct: this may have the advantage of better communicating intent by placing bounds on the loader types themselves. -
As for the implementation, I've gone down the somewhat 'hacky' road of taking the lowest common denominator and making the whole parsing process async. To make it compatible with both sync and async readers, I've added two new internal traits:
Reader, which abstractsquick_xml::Readerand delegates to eitherquick_xml::Reader::read_event_intoorquick_xml::Reader::read_event_into_asyncdepending on the implementor, andReadFrom, which abstractsResourceReaderandAsyncResourceReaderby providing aReader. On the other end, to get back to the sync world from the async parsing,Loader::load_tmx_mapandLoader::load_tsx_tileset'execute' the futures withFutureExt::now_or_neverfrom thefuturescrate.
Otherwise, further work on this solution includes:
- Properly documenting everything
- Testing with an actual async reader (though there's almost no room for new errors as sync loading also uses the async parser) and maybe examples
- Feature-gating async functionality
So it's not quite ready yet.