mindat_rs/lib.rs
1//! # mindat-rs
2//!
3//! A Rust client library for the [Mindat API](https://api.mindat.org/).
4//!
5//! Mindat is the world's largest open database of minerals, rocks, meteorites, and
6//! the localities where they come from. This crate provides a type-safe, async interface
7//! to access mineralogical data.
8//!
9//! ## Features
10//!
11//! - Full coverage of the Mindat API endpoints
12//! - Strongly-typed request builders and response models
13//! - Async/await support using tokio
14//! - Pagination helpers
15//! - Comprehensive error handling
16//!
17//! ## Quick Start
18//!
19//! ```no_run
20//! use mindat_rs::{MindatClient, GeomaterialsQuery, Result};
21//!
22//! #[tokio::main]
23//! async fn main() -> Result<()> {
24//! // Create a client with your API token
25//! let client = MindatClient::new("your-api-token");
26//!
27//! // Search for quartz
28//! let query = GeomaterialsQuery::new()
29//! .name("quartz")
30//! .ima_approved(true);
31//!
32//! let minerals = client.geomaterials(query).await?;
33//!
34//! for mineral in minerals.results {
35//! println!("{}: {:?}", mineral.id, mineral.name);
36//! }
37//!
38//! Ok(())
39//! }
40//! ```
41//!
42//! ## Authentication
43//!
44//! Most API endpoints require authentication with a Mindat API token.
45//! You can obtain a token from your [Mindat account settings](https://www.mindat.org/).
46//!
47//! Some endpoints (like `minerals_ima`) can be accessed without authentication:
48//!
49//! ```no_run
50//! use mindat_rs::{MindatClient, ImaMineralsQuery};
51//!
52//! # async fn example() -> mindat_rs::Result<()> {
53//! let client = MindatClient::anonymous();
54//! let minerals = client.minerals_ima(ImaMineralsQuery::new()).await?;
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! ## Pagination
60//!
61//! Most list endpoints return paginated results. Use the pagination helpers:
62//!
63//! ```no_run
64//! use mindat_rs::{MindatClient, GeomaterialsQuery};
65//!
66//! # async fn example() -> mindat_rs::Result<()> {
67//! let client = MindatClient::new("token");
68//!
69//! // Get first page
70//! let query = GeomaterialsQuery::new().page(1).page_size(100);
71//! let page1 = client.geomaterials(query).await?;
72//!
73//! // Check if there are more pages
74//! if page1.has_next() {
75//! let query = GeomaterialsQuery::new().page(2).page_size(100);
76//! let page2 = client.geomaterials(query).await?;
77//! }
78//! # Ok(())
79//! # }
80//! ```
81//!
82//! ## Available Endpoints
83//!
84//! - **Countries**: List and retrieve country information
85//! - **Geomaterials**: Search minerals, rocks, varieties, synonyms, and more
86//! - **Localities**: Search mineral localities worldwide
87//! - **IMA Minerals**: Access IMA-approved mineral species
88//! - **Classification**: Dana 8th ed. and Nickel-Strunz 10th ed. systems
89//! - **Locality Metadata**: Ages, statuses, types, and geographic regions
90
91pub mod client;
92pub mod error;
93pub mod models;
94
95pub use client::{DEFAULT_BASE_URL, MindatClient, MindatClientBuilder};
96pub use error::{MindatError, Result};
97pub use models::*;
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_client_creation() {
105 let client = MindatClient::new("test-token");
106 assert_eq!(client.base_url().as_str(), "https://api.mindat.org/v1/");
107 }
108
109 #[test]
110 fn test_anonymous_client() {
111 let client = MindatClient::anonymous();
112 assert_eq!(client.base_url().as_str(), "https://api.mindat.org/v1/");
113 }
114
115 #[test]
116 fn test_geomaterials_query_builder() {
117 let query = GeomaterialsQuery::new()
118 .name("quartz")
119 .ima_approved(true)
120 .with_elements("Si,O")
121 .hardness_range(6.0, 7.0)
122 .page(1)
123 .page_size(50);
124
125 assert_eq!(query.name, Some("quartz".to_string()));
126 assert_eq!(query.ima, Some(true));
127 assert_eq!(query.elements_inc, Some("Si,O".to_string()));
128 assert_eq!(query.hardness_min, Some(6.0));
129 assert_eq!(query.hardness_max, Some(7.0));
130 assert_eq!(query.page, Some(1));
131 assert_eq!(query.page_size, Some(50));
132 }
133
134 #[test]
135 fn test_localities_query_builder() {
136 let query = LocalitiesQuery::new()
137 .country("Brazil")
138 .with_elements("Au")
139 .select_fields("id,txt,country");
140
141 assert_eq!(query.country, Some("Brazil".to_string()));
142 assert_eq!(query.elements_inc, Some("Au".to_string()));
143 assert_eq!(query.fields, Some("id,txt,country".to_string()));
144 }
145
146 #[test]
147 fn test_paginated_response() {
148 let response: PaginatedResponse<i32> = PaginatedResponse {
149 count: Some(150),
150 next: Some("https://api.mindat.org/geomaterials/?page=2".to_string()),
151 previous: None,
152 results: vec![1, 2, 3],
153 };
154
155 assert!(response.has_next());
156 assert!(!response.has_previous());
157 assert_eq!(response.total_pages(50), Some(3));
158 }
159
160 #[test]
161 fn test_entry_type_from_u8() {
162 assert_eq!(EntryType::from(0), EntryType::Mineral);
163 assert_eq!(EntryType::from(1), EntryType::Synonym);
164 assert_eq!(EntryType::from(2), EntryType::Variety);
165 assert_eq!(EntryType::from(7), EntryType::Rock);
166 assert_eq!(EntryType::from(99), EntryType::Mineral); // Unknown defaults to Mineral
167 }
168
169 #[test]
170 fn test_geomaterials_ordering_display() {
171 assert_eq!(GeomaterialsOrdering::Id.to_string(), "id");
172 assert_eq!(GeomaterialsOrdering::IdDesc.to_string(), "-id");
173 assert_eq!(GeomaterialsOrdering::Name.to_string(), "name");
174 }
175}