-
Notifications
You must be signed in to change notification settings - Fork 110
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-rs
toquick-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-rs
expects aRead
whilequick-xml
expects aBufRead
. Accordingly, I've changed theRead
bound onResourceReader::Resource
toBufRead
. This is technically a breaking change, but is worth it in my opinion: most people probably only useFilesystemResourceReader
orstd::io::Cursor
, which would both still work. Otherwise, wrapping a reader instd::io::BufReader
should not be a difficult change (and of course, we're before 1.0). -
Added an
AsyncResourceReader
trait, mirroringResourceReader
, that asynchronously provides atokio::io::AsyncBufRead
. -
Removed the
ResourceReader
bound onLoader
and moved it to the loading functions to which I added async counterparts. The alternative would be to create a separateAsyncLoader
struct: 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::Reader
and delegates to eitherquick_xml::Reader::read_event_into
orquick_xml::Reader::read_event_into_async
depending on the implementor, andReadFrom
, which abstractsResourceReader
andAsyncResourceReader
by providing aReader
. On the other end, to get back to the sync world from the async parsing,Loader::load_tmx_map
andLoader::load_tsx_tileset
'execute' the futures withFutureExt::now_or_never
from thefutures
crate.
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.