modio 0.14.2

Rust interface for integrating https://mod.io - a modding API for game developers
Documentation
use std::future::{Future, IntoFuture};
use std::pin::Pin;
use std::task::{ready, Context, Poll};

use futures_util::future::Either;
use http::StatusCode;

use crate::client::Client;
use crate::request::files::filters::Version;
use crate::request::files::GetFiles;
use crate::request::filter::prelude::*;
use crate::response::{DataFuture, ResponseFuture};
use crate::types::files::File;
use crate::types::id::{GameId, ModId};
use crate::types::List;
use crate::util::download::{Error, ErrorKind, ResolvePolicy};

pin_project_lite::pin_project! {
    pub struct GetFileByVersion {
        game_id: GameId,
        mod_id: ModId,
        version: String,
        policy: ResolvePolicy,
        #[pin]
        future: Either<ResponseFuture<List<File>>, DataFuture<List<File>>>,
    }
}

impl GetFileByVersion {
    pub(crate) fn new(
        http: &Client,
        game_id: GameId,
        mod_id: ModId,
        version: String,
        policy: ResolvePolicy,
    ) -> Self {
        let filter = Version::eq(version.clone())
            .order_by(DateAdded::desc())
            .limit(2);

        let fut = GetFiles::new(http, game_id, mod_id)
            .filter(filter)
            .into_future();

        Self {
            game_id,
            mod_id,
            version,
            policy,
            future: Either::Left(fut),
        }
    }
}

impl Future for GetFileByVersion {
    type Output = Result<File, Error>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.project();
        loop {
            match this.future.as_mut().as_pin_mut() {
                Either::Left(fut) => {
                    let resp = ready!(fut.poll(cx)).map_err(Error::request)?;

                    if resp.status() == StatusCode::NOT_FOUND {
                        let kind = ErrorKind::ModNotFound {
                            game_id: *this.game_id,
                            mod_id: *this.mod_id,
                        };
                        return Poll::Ready(Err(Error::new(kind)));
                    }

                    this.future.set(Either::Right(resp.data()));
                }
                Either::Right(fut) => {
                    let mut list = match fut.poll(cx) {
                        Poll::Ready(Ok(list)) => list.data,
                        Poll::Ready(Err(err)) => return Poll::Ready(Err(Error::body(err))),
                        Poll::Pending => return Poll::Pending,
                    };

                    let result = match (list.len(), &this.policy) {
                        (1, _) | (_, ResolvePolicy::Latest) => Ok(list.remove(0)),
                        (0, _) => Err({
                            let kind = ErrorKind::VersionNotFound {
                                game_id: *this.game_id,
                                mod_id: *this.mod_id,
                                version: this.version.clone(),
                            };
                            Error::new(kind)
                        }),
                        (_, ResolvePolicy::Fail) => Err({
                            let kind = ErrorKind::MultipleFilesFound {
                                game_id: *this.game_id,
                                mod_id: *this.mod_id,
                                version: this.version.clone(),
                            };
                            Error::new(kind)
                        }),
                    };

                    return Poll::Ready(result);
                }
            }
        }
    }
}