phabricator 0.0.4

Crate to work with Phabricator web APIs
Documentation
use crate::search;
use crate::Client;
use crate::Project;
use futures::prelude::*;
use futures::Stream;
use phabricator_api::project::search::Search;
use phabricator_api::project::search::SearchCursor;
use phabricator_api::types::Cursor;
use phabricator_api::types::Phid;
use phabricator_api::RequestError;
use std::sync::Arc;

async fn get(
    client: &Client,
    search: Arc<Search>,
    cursor: Option<Cursor>,
) -> Result<search::QueryData<Project>, RequestError> {
    let mut data = match cursor {
        Some(cursor) => {
            let s = SearchCursor {
                cursor: &cursor,
                search: &*search,
            };
            client.client().request(&s).await?
        }
        None => client.client().request(&*search).await?,
    };

    let projects = data
        .data
        .drain(..)
        .map(|d| Project::update_from_searchdata(d, client))
        .collect();

    let cursor = if data.cursor.after.is_some() {
        Some(data.cursor)
    } else {
        None
    };

    Ok((projects, cursor))
}

pub struct ProjectsBuilder<'c, P> {
    client: &'c Client,
    phids: P,
}

impl<'a, 'c, P> ProjectsBuilder<'c, P>
where
    P: IntoIterator<Item = &'a Phid>,
{
    pub(crate) fn new(client: &'c Client, phids: P) -> Self {
        ProjectsBuilder { client, phids }
    }

    pub fn query(self) -> impl Stream<Item = Result<Project, RequestError>> + 'c {
        let client = &self.client;
        let (lookup, cached) =
            self.phids
                .into_iter()
                .fold((vec![], vec![]), |(mut lookup, mut cached), p| {
                    let project = client.cached_project(p);

                    match project {
                        Some(prj) => cached.push(prj),
                        None => lookup.push(p.clone()),
                    }
                    (lookup, cached)
                });

        let search = if lookup.is_empty() {
            None
        } else {
            let mut search: Search = Default::default();
            search.constraints.phids = Some(lookup);
            Some(search)
        };

        search::Search::new(
            self.client,
            cached,
            search,
            Box::new(|client, search, cursor| get(client, search, cursor).boxed()),
        )
    }
}