quick_oxibooks/lib.rs
1//! # Quick Oxibooks Library
2//!
3//! **Quick Oxibooks** is a comprehensive Rust library for interacting with the `QuickBooks` Online API.
4//! It provides a strongly-typed, rate-limited, and feature-rich interface for performing CRUD operations,
5//! batch processing, and generating reports with `QuickBooks` data.
6//!
7//! ## Key Features
8//!
9//! - **Complete CRUD Operations**: Create, read, update, delete, and query `QuickBooks` entities
10//! - **Batch Processing**: Execute multiple operations in a single API call for improved performance
11//! - **Rate Limiting**: Built-in rate limiting to respect `QuickBooks` API limits
12//! - **Strong Typing**: All `QuickBooks` entities are strongly typed with validation
13//! - **PDF Generation**: Generate PDFs for supported entities (invoices, estimates, etc.)
14//! - **Attachment Support**: Upload and manage file attachments
15//! - **Report Generation**: Access `QuickBooks` financial reports (P&L, Balance Sheet, etc.)
16//! - **Macro Support**: Convenient macros for building queries
17//!
18//! ## Quick Start
19//!
20//! ```no_run
21//! use quick_oxibooks::{QBContext, Environment};
22//! use quickbooks_types::{Customer, Invoice};
23//! use quick_oxibooks::functions::{create::QBCreate, query::QBQuery, read::QBRead};
24//! use ureq::Agent;
25//!
26//! // Create a QuickBooks context
27//! let client = Agent::new_with_defaults();
28//! let qb_context = QBContext::new(
29//! Environment::SANDBOX,
30//! "your_company_id".to_string(),
31//! "your_access_token".to_string(),
32//! &client
33//! ).unwrap();
34//!
35//! // Create a new customer
36//! let mut customer = Customer::default();
37//! customer.display_name = Some("John Doe".to_string());
38//! let created_customer = customer.create(&qb_context, &client).unwrap();
39//!
40//! // Query for invoices
41//! let invoices = Invoice::query("WHERE TotalAmt > '100.00'", Some(10), &qb_context, &client).unwrap();
42//!
43//! // Read a specific invoice by ID
44//! let invoice = Invoice::query_single("WHERE Id = '123'", &qb_context, &client).unwrap();
45//! ```
46//! ## Features
47//!
48//! - `attachments`: Enables file attachment upload and management functions
49//! - `pdf`: Enables PDF generation for supported `QuickBooks` entities
50//! - `macros`: Enables convenient query-building macros
51//! - `polars`: Enables integration with the `polars` `DataFrame` library for data analysis
52//! - `logging`: Enables detailed logging of API requests and responses
53//!
54//! For more detailed usage examples, refer to the documentation for each module and type.
55#![warn(clippy::pedantic)]
56
57pub mod batch;
58pub mod client;
59pub use client::QBContext;
60use error::APIError;
61use serde::{Deserialize, Serialize};
62use ureq::Agent;
63pub mod error;
64
65pub mod types {
66 //! Re-exports of all types from the `quickbooks_types` crate.
67 pub use quickbooks_types::*;
68}
69
70pub mod functions;
71pub(crate) mod limiter;
72
73use crate::error::APIErrorInner;
74#[cfg(feature = "attachments")]
75pub use crate::functions::attachment;
76#[cfg(feature = "pdf")]
77pub use crate::functions::pdf;
78#[cfg(feature = "macros")]
79pub mod macros;
80
81/// The result type used throughout the library for operations that may fail.
82///
83/// This is a type alias for `Result<T, APIError>` where `APIError` contains
84/// detailed information about what went wrong during API operations.
85///
86/// # Examples
87///
88/// ```no_run
89/// use quick_oxibooks::{APIResult, QBContext, Environment};
90/// use ureq::Agent;
91///
92/// fn get_context() -> APIResult<QBContext> {
93/// let client = Agent::new_with_defaults();
94/// QBContext::new_from_env(Environment::SANDBOX, &client)
95/// }
96/// ```
97pub type APIResult<T> = Result<T, APIError>;
98
99/// Represents the `QuickBooks` API environment.
100///
101/// `QuickBooks` provides two environments:
102/// - **SANDBOX**: For development and testing, uses sandbox URLs and data
103/// - **PRODUCTION**: For live applications, uses production URLs and real data
104///
105/// The environment determines which API endpoints are used for all operations.
106///
107/// # Examples
108///
109/// ```rust
110/// use quick_oxibooks::Environment;
111///
112/// // For development
113/// let env = Environment::SANDBOX;
114///
115/// // For production
116/// let env = Environment::PRODUCTION;
117///
118/// // Default is SANDBOX for safety
119/// let default_env = Environment::default();
120/// assert_eq!(default_env, Environment::SANDBOX);
121/// ```
122#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
123pub enum Environment {
124 /// Production environment for live `QuickBooks` data
125 PRODUCTION,
126 /// Sandbox environment for development and testing (default)
127 #[default]
128 SANDBOX,
129}
130
131impl Environment {
132 /// Returns the OAuth 2.0 discovery URL for the environment.
133 ///
134 /// The discovery URL provides OAuth endpoints and configuration for authentication.
135 ///
136 /// # Returns
137 ///
138 /// A static string containing the discovery URL for the environment.
139 #[inline]
140 #[must_use]
141 pub fn discovery_url(&self) -> &'static str {
142 match self {
143 Environment::PRODUCTION => {
144 "https://developer.intuit.com/.well-known/openid_configuration/"
145 }
146 Environment::SANDBOX => {
147 "https://developer.intuit.com/.well-known/openid_sandbox_configuration/"
148 }
149 }
150 }
151
152 /// Returns the token migration URL for the environment.
153 ///
154 /// Used for migrating OAuth 1.0 tokens to OAuth 2.0.
155 ///
156 /// # Returns
157 ///
158 /// A static string containing the migration URL for the environment.
159 #[inline]
160 #[must_use]
161 pub fn migration_url(&self) -> &'static str {
162 match self {
163 Environment::PRODUCTION => {
164 "https://developer-sandbox.api.intuit.com/v2/oauth2/tokens/migrate"
165 }
166 Environment::SANDBOX => "https://developer.api.intuit.com/v2/oauth2/tokens/migrate",
167 }
168 }
169
170 /// Returns the user info URL for the environment.
171 ///
172 /// Used to retrieve user information from the `OpenID` Connect userinfo endpoint.
173 ///
174 /// # Returns
175 ///
176 /// A static string containing the user info URL for the environment.
177 #[inline]
178 #[must_use]
179 pub fn user_info_url(&self) -> &'static str {
180 match self {
181 Environment::PRODUCTION => {
182 "https://accounts.platform.intuit.com/v1/openid_connect/userinfo"
183 }
184 Environment::SANDBOX => {
185 "https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo"
186 }
187 }
188 }
189
190 /// Returns the base API endpoint URL for the environment.
191 ///
192 /// This is the root URL for all `QuickBooks` API operations (CRUD, queries, reports, etc.).
193 ///
194 /// # Returns
195 ///
196 /// A static string containing the API endpoint URL for the environment.
197 #[inline]
198 #[must_use]
199 pub fn endpoint_url(&self) -> &'static str {
200 match self {
201 Environment::PRODUCTION => "https://quickbooks.api.intuit.com/v3/",
202 Environment::SANDBOX => "https://sandbox-quickbooks.api.intuit.com/v3/",
203 }
204 }
205}
206
207/// OAuth 2.0 discovery document for `QuickBooks` API.
208///
209/// Contains OAuth 2.0 endpoint URLs and supported capabilities discovered from
210/// the `QuickBooks` OAuth discovery endpoint. This is automatically fetched when
211/// creating a [`QBContext`] and used for authentication flows.
212///
213/// # Fields
214///
215/// - `issuer`: The OAuth 2.0 issuer identifier
216/// - `authorization_endpoint`: URL for user authorization
217/// - `token_endpoint`: URL for token exchange
218/// - `userinfo_endpoint`: URL for retrieving user information
219/// - `revocation_endpoint`: URL for token revocation
220/// - `jwks_uri`: URL for JSON Web Key Set
221/// - Various supported capabilities arrays
222///
223/// # Examples
224///
225/// ```no_run
226/// use quick_oxibooks::{DiscoveryDoc, Environment};
227/// use ureq::Agent;
228///
229/// let client = Agent::new_with_defaults();
230/// let discovery = DiscoveryDoc::get(Environment::SANDBOX, &client).unwrap();
231/// println!("Token endpoint: {}", discovery.token_endpoint);
232/// ```
233#[derive(Deserialize, Debug, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
234#[allow(unused)]
235pub struct DiscoveryDoc {
236 pub issuer: String,
237 pub authorization_endpoint: String,
238 pub token_endpoint: String,
239 pub userinfo_endpoint: String,
240 pub revocation_endpoint: String,
241 pub jwks_uri: String,
242 pub response_types_supported: Vec<String>,
243 pub subject_types_supported: Vec<String>,
244 pub id_token_signing_alg_values_supported: Vec<String>,
245 pub scopes_supported: Vec<String>,
246 pub token_endpoint_auth_methods_supported: Vec<String>,
247 pub claims_supported: Vec<String>,
248}
249
250impl DiscoveryDoc {
251 /// Fetches the OAuth 2.0 discovery document from `QuickBooks`.
252 ///
253 /// This method makes an HTTP request to the `QuickBooks` discovery endpoint
254 /// to retrieve OAuth 2.0 configuration and supported capabilities.
255 ///
256 /// # Parameters
257 ///
258 /// - `environment`: The `QuickBooks` environment (sandbox or production)
259 /// - `client`: HTTP client for making the request
260 ///
261 /// # Returns
262 ///
263 /// Returns the parsed discovery document on success, or an [`APIError`] on failure.
264 ///
265 /// # Errors
266 ///
267 /// - Network errors when fetching the discovery document
268 /// - JSON parsing errors if the response format is invalid
269 /// - HTTP errors if the discovery endpoint returns an error response
270 ///
271 /// # Examples
272 ///
273 /// ```no_run
274 /// use quick_oxibooks::{DiscoveryDoc, Environment};
275 /// use ureq::Agent;
276 ///
277 /// let client = Agent::new_with_defaults();
278 /// let discovery = DiscoveryDoc::get(Environment::SANDBOX, &client).unwrap();
279 /// ```
280 pub fn get(environment: Environment, client: &Agent) -> APIResult<Self> {
281 let url = environment.discovery_url();
282 let request = client.get(url).call()?;
283 if !request.status().is_success() {
284 return Err(APIErrorInner::BadRequest(request.into_body().read_json()?).into());
285 }
286 Ok(request.into_body().read_json()?)
287 }
288}