nolgia 0.1.0

Official Rust SDK for the Nolgia API: image, video, audio and agent generation, generated from the OpenAPI spec
Documentation
//! A synchronous client, for programs without an async runtime (feature
//! `blocking`).
//!
//! ```no_run
//! fn main() -> nolgia::Result<()> {
//!     let nolgia = nolgia::blocking::Client::from_env()?;
//!     let done = nolgia.subscribe(nolgia::image("gpt-image-2", "a red fox in the snow"))?;
//!     nolgia.download(done.url().unwrap(), "fox.png")?;
//!     Ok(())
//! }
//! ```
//!
//! Every generated endpoint is reachable through [`Client::block_on`]:
//! `nolgia.block_on(nolgia.api().list_models().send().typed())`.
//!
//! Do not use it from inside an async runtime (a `#[tokio::main]` function,
//! say): blocking there panics. Use the async [`crate::Client`] instead.

use std::{future::Future, path::Path, sync::Arc};

use uuid::Uuid;

use crate::{
    AgentRunResult, GenerationResult, IntoGeneration, RunAgentOptions, SubmitOptions,
    UploadOptions, WaitOptions,
    api::types,
    error::{Error, Result},
};

/// The blocking twin of [`crate::Client`].
#[derive(Debug, Clone)]
pub struct Client {
    inner: crate::Client,
    runtime: Arc<tokio::runtime::Runtime>,
}

impl Client {
    /// A production client authenticated with `api_key`.
    pub fn new(api_key: impl Into<String>) -> Result<Self> {
        Self::from_async(crate::Client::new(api_key)?)
    }

    /// A client from `NOLGIA_API_KEY` (or `NOLGIA_TOKEN`) and `NOLGIA_BASE_URL`.
    pub fn from_env() -> Result<Self> {
        Self::from_async(crate::Client::from_env()?)
    }

    /// Wrap an async client built with [`crate::Client::builder`].
    pub fn from_async(inner: crate::Client) -> Result<Self> {
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .map_err(|err| Error::Config(format!("could not start a runtime: {err}")))?;
        Ok(Self {
            inner,
            runtime: Arc::new(runtime),
        })
    }

    /// The async client inside, for its generated endpoint methods.
    pub fn api(&self) -> &crate::Client {
        &self.inner
    }

    /// Run any future (a generated call, say) to completion.
    pub fn block_on<F: Future>(&self, future: F) -> F::Output {
        self.runtime.block_on(future)
    }

    /// See [`crate::Client::submit`]. Returns the queued job.
    pub fn submit(&self, request: impl IntoGeneration) -> Result<types::Job> {
        self.block_on(async { Ok(self.inner.submit(request).await?.job().clone()) })
    }

    /// See [`crate::Client::submit_with`].
    pub fn submit_with(
        &self,
        request: impl IntoGeneration,
        options: SubmitOptions,
    ) -> Result<types::Job> {
        self.block_on(async {
            Ok(self
                .inner
                .submit_with(request, options)
                .await?
                .job()
                .clone())
        })
    }

    /// See [`crate::Client::subscribe`].
    pub fn subscribe(&self, request: impl IntoGeneration) -> Result<GenerationResult> {
        self.block_on(self.inner.subscribe(request))
    }

    /// See [`crate::Client::quote`].
    pub fn quote(&self, request: impl IntoGeneration) -> Result<types::JobCostQuote> {
        self.block_on(self.inner.quote(request))
    }

    /// See [`crate::Client::wait_job`].
    pub fn wait_job(&self, job_id: Uuid, options: WaitOptions) -> Result<types::Job> {
        self.block_on(self.inner.wait_job(job_id, options))
    }

    /// See [`crate::Client::job_media`].
    pub fn job_media(&self, job: &types::Job) -> Vec<crate::Media> {
        self.block_on(self.inner.job_media(job))
    }

    /// Cancel a job on the server.
    pub fn cancel_job(&self, job_id: Uuid) -> Result<types::Job> {
        use crate::Typed;
        self.block_on(self.inner.cancel_job().id(job_id).send().typed())
    }

    /// See [`crate::Client::upload_file`].
    pub fn upload_file(
        &self,
        path: impl AsRef<Path>,
        options: UploadOptions,
    ) -> Result<types::Asset> {
        self.block_on(self.inner.upload_file(path, options))
    }

    /// See [`crate::Client::download`].
    pub fn download(&self, url: &str, path: impl AsRef<Path>) -> Result<u64> {
        self.block_on(self.inner.download(url, path))
    }

    /// See [`crate::Client::download_bytes`].
    pub fn download_bytes(&self, url: &str) -> Result<Vec<u8>> {
        self.block_on(self.inner.download_bytes(url))
    }

    /// See [`crate::Client::run_agent`].
    pub fn run_agent(&self, options: RunAgentOptions) -> Result<AgentRunResult> {
        self.block_on(self.inner.run_agent(options))
    }

    /// Every asset matching the filters, fetched a page at a time as the
    /// iterator is read.
    pub fn assets<'a>(
        &'a self,
        configure: impl Fn(crate::api::builder::ListAssets<'a>) -> crate::api::builder::ListAssets<'a>
        + 'a,
    ) -> impl Iterator<Item = Result<types::Asset>> + 'a {
        let mut stream = Box::pin(self.inner.paginate_assets(configure));
        std::iter::from_fn(move || {
            use futures_util::StreamExt;
            self.runtime.block_on(stream.next())
        })
    }
}