pub struct CkanClient { /* private fields */ }Expand description
Async CKAN client focused on data.gov’s read APIs.
CkanClient wraps the generated request glue and exposes ergonomic async
methods for popular CKAN endpoints such as package_search,
package_show, organization listings, and autocomplete helpers. The client
is cheap to clone and share across tasks because it only holds an
Arc<Configuration>.
use data_gov_ckan::{CkanClient, Configuration};
use std::sync::Arc;
let client = CkanClient::new(Arc::new(Configuration::default()));
let results = client.package_search(Some("climate"), Some(5), None, None).await?;
println!("{} datasets", results.count.unwrap_or(0));Implementations§
Source§impl CkanClient
impl CkanClient
Sourcepub fn new(configuration: Arc<Configuration>) -> Self
pub fn new(configuration: Arc<Configuration>) -> Self
Create a new CKAN client instance
Creates a client configured to work with a specific CKAN instance.
For data.gov, use the base URL: https://catalog.data.gov/api/3
§Arguments
configuration- API configuration including base URL, user agent, and credentials
§Examples
// Basic client for read-only operations
let config = Arc::new(Configuration {
base_path: "https://catalog.data.gov/api/3".to_string(),
user_agent: Some("my-rust-app/1.0".to_string()),
client: reqwest::Client::new(),
basic_auth: None,
oauth_access_token: None,
bearer_access_token: None,
api_key: None,
});
let client = CkanClient::new(config);
// Client with API key for write operations
let authenticated_config = Arc::new(Configuration {
base_path: "https://catalog.data.gov/api/3".to_string(),
user_agent: Some("my-rust-app/1.0".to_string()),
client: reqwest::Client::new(),
basic_auth: None,
oauth_access_token: None,
bearer_access_token: None,
api_key: Some(ApiKey {
prefix: None,
key: "your-api-key-here".to_string(),
}),
});
let auth_client = CkanClient::new(authenticated_config);Sourcepub async fn package_search(
&self,
q: Option<&str>,
rows: Option<i32>,
start: Option<i32>,
fq: Option<&str>,
) -> Result<PackageSearchResult, CkanError>
pub async fn package_search( &self, q: Option<&str>, rows: Option<i32>, start: Option<i32>, fq: Option<&str>, ) -> Result<PackageSearchResult, CkanError>
Search for datasets (packages) with advanced filtering and faceting
This is the primary method for discovering datasets in the CKAN catalog. It provides powerful search capabilities with full-text search, filtering, faceting, and pagination.
§Arguments
q- Search query string (searches title, description, tags, etc.)rows- Maximum number of results to return (default: 10, max typically: 1000)start- Starting offset for pagination (0-based)fq- Additional filter queries in Solr format
§Returns
Returns search results with datasets, facets, and pagination information.
§Examples
// Basic text search
let results = client.package_search(
Some("climate change"),
Some(20),
Some(0),
None
).await?;
println!("Found {} total datasets", results.count.unwrap_or(0));
for package in results.results.unwrap_or_default() {
println!("Title: {}", package.title.unwrap_or_default());
println!("Organization: {}", package.organization.as_ref()
.and_then(|org| org.title.as_deref())
.unwrap_or("Unknown"));
}
// Search with organization filter
let epa_datasets = client.package_search(
Some("water quality"),
Some(10),
Some(0),
Some("organization:epa-gov")
).await?;
// Search with multiple filters
let recent_climate_data = client.package_search(
Some("climate"),
Some(15),
Some(0),
Some("res_format:CSV AND metadata_modified:[2020-01-01T00:00:00Z TO NOW]")
).await?;§Pagination
// Paginate through all results
let mut start = 0;
let page_size = 50;
loop {
let results = client.package_search(
Some("energy"),
Some(page_size),
Some(start),
None
).await?;
let packages = results.results.unwrap_or_default();
if packages.is_empty() {
break;
}
// Process this page of results
for package in packages {
println!("Processing: {}", package.name);
}
start += page_size;
}§Advanced Filtering
The fq parameter supports Solr query syntax for advanced filtering. The
q and fq parameters are passed through to CKAN’s Solr-backed
package_search endpoint, so you can use familiar Solr constructs such as:
organization:epa-gov- Filter by organizationres_format:CSV- Filter by resource formattags:healthcare- Filter by tagsmetadata_modified:[2020-01-01T00:00:00Z TO NOW]- Date ranges- Combine with
AND,OR,NOToperators
Examples:
- Text search with wildcard:
q=climat* - Phrase search:
q="air quality" - Complex filter:
fq=organization:epa-gov AND res_format:CSV
Sourcepub async fn package_show(&self, id: &str) -> Result<Package, CkanError>
pub async fn package_show(&self, id: &str) -> Result<Package, CkanError>
Retrieve a specific dataset by its ID or name
Fetches complete metadata for a single dataset, including all resources, tags, organization details, and custom metadata fields.
§Arguments
id- Dataset ID (UUID) or name (URL-friendly slug)
§Returns
Returns the complete dataset record with all metadata and resources.
§Examples
// Get dataset by name
let dataset = client.package_show("consumer-complaint-database").await?;
println!("Dataset: {}", dataset.title.unwrap_or_default());
println!("Description: {}", dataset.notes.unwrap_or_default());
// List all resources in the dataset
println!("Resources:");
for resource in dataset.resources.unwrap_or_default() {
println!(" - {} ({})",
resource.name.as_deref().unwrap_or("Unnamed"),
resource.format.as_deref().unwrap_or("Unknown format")
);
if let Some(url) = resource.url {
println!(" URL: {}", url);
}
if let Some(size) = resource.size {
println!(" Size: {} bytes", size);
}
}
// Show dataset tags
if let Some(tags) = dataset.tags {
let tag_names: Vec<String> = tags.into_iter()
.filter_map(|tag| tag.display_name)
.collect();
println!("Tags: {}", tag_names.join(", "));
}
// Show organization
if let Some(org) = dataset.organization {
println!("Organization: {}", org.title.unwrap_or_default());
}
// Get dataset by UUID
let dataset_by_id = client.package_show("a1b2c3d4-e5f6-7890-abcd-ef1234567890").await?;§Error Handling
match client.package_show("nonexistent-dataset").await {
Ok(dataset) => {
println!("Found dataset: {}", dataset.title.unwrap_or_default());
},
Err(CkanError::ApiError { status: 404, .. }) => {
println!("Dataset not found");
},
Err(e) => {
println!("Other error: {}", e);
}
}§Dataset Metadata
The returned dataset includes rich metadata:
- Basic Info: Title, description, notes, license
- Resources: Files, APIs, documentation associated with dataset
- Organization: Publishing agency/department information
- Tags: Subject tags and keywords for discovery
- Temporal: Creation date, modification date, temporal coverage
- Spatial: Geographic coverage and bounding boxes
- Custom Fields: Agency-specific metadata extensions
Sourcepub async fn organization_list(
&self,
sort: Option<&str>,
limit: Option<i32>,
offset: Option<i32>,
) -> Result<Vec<String>, CkanError>
pub async fn organization_list( &self, sort: Option<&str>, limit: Option<i32>, offset: Option<i32>, ) -> Result<Vec<String>, CkanError>
List all organizations in the CKAN instance
Sourcepub async fn group_list(
&self,
sort: Option<&str>,
limit: Option<i32>,
offset: Option<i32>,
) -> Result<Vec<String>, CkanError>
pub async fn group_list( &self, sort: Option<&str>, limit: Option<i32>, offset: Option<i32>, ) -> Result<Vec<String>, CkanError>
List all groups in the CKAN instance
Sourcepub async fn dataset_autocomplete(
&self,
incomplete: Option<&str>,
limit: Option<i32>,
) -> Result<Vec<DatasetAutocomplete>, CkanError>
pub async fn dataset_autocomplete( &self, incomplete: Option<&str>, limit: Option<i32>, ) -> Result<Vec<DatasetAutocomplete>, CkanError>
Get dataset autocomplete suggestions for type-ahead functionality
Provides quick dataset name/title suggestions as the user types, perfect for implementing search boxes with autocomplete dropdowns.
§Arguments
incomplete- Partial dataset name or title to search for (e.g., “climat”)limit- Maximum number of suggestions to return (default: 10, reasonable max: 20)
§Returns
Returns suggestions containing dataset names and titles that match the input.
§Examples
// Get suggestions as user types "elect"
let suggestions = client.dataset_autocomplete(Some("elect"), Some(5)).await?;
for suggestion in &suggestions {
println!("Dataset: {} - {}",
suggestion.name.as_deref().unwrap_or("Unknown"),
suggestion.title.as_deref().unwrap_or("No title"));
}§UI Integration
This is designed for real-time search suggestions:
// In your web frontend or CLI app
async fn on_search_input_change(input: &str, client: &CkanClient) -> Result<(), Box<dyn std::error::Error>> {
if input.len() >= 2 { // Start suggesting after 2+ characters
let suggestions = client.dataset_autocomplete(Some(input), Some(10)).await?;
// Display suggestions in dropdown/list
for suggestion in &suggestions {
println!("Suggestion: {}", suggestion.title.as_deref().unwrap_or("Unknown"));
}
}
Ok(())
}Sourcepub async fn tag_autocomplete(
&self,
incomplete: Option<&str>,
limit: Option<i32>,
vocabulary_id: Option<&str>,
) -> Result<Vec<String>, CkanError>
pub async fn tag_autocomplete( &self, incomplete: Option<&str>, limit: Option<i32>, vocabulary_id: Option<&str>, ) -> Result<Vec<String>, CkanError>
Get tag autocomplete suggestions
Sourcepub async fn user_autocomplete(
&self,
q: Option<&str>,
limit: Option<i32>,
ignore_self: Option<bool>,
) -> Result<Vec<UserAutocomplete>, CkanError>
pub async fn user_autocomplete( &self, q: Option<&str>, limit: Option<i32>, ignore_self: Option<bool>, ) -> Result<Vec<UserAutocomplete>, CkanError>
Get user autocomplete suggestions
Sourcepub async fn group_autocomplete(
&self,
q: Option<&str>,
limit: Option<i32>,
) -> Result<Vec<GroupAutocomplete>, CkanError>
pub async fn group_autocomplete( &self, q: Option<&str>, limit: Option<i32>, ) -> Result<Vec<GroupAutocomplete>, CkanError>
Get group autocomplete suggestions for filtering and organization
Groups in CKAN represent thematic collections of datasets. This endpoint provides autocomplete functionality for group names and titles.
§Arguments
q- Partial group name or title to search for (e.g., “agri”)limit- Maximum number of suggestions to return (default: 10)
§Returns
Returns group suggestions with names, display names, and metadata.
§Examples
// Find agriculture-related groups
let groups = client.group_autocomplete(Some("agri"), Some(5)).await?;
println!("Found {} groups", groups.len());
for group in groups {
println!("Group: {} ({})",
group.title.as_deref().unwrap_or("Unknown"),
group.name.as_deref().unwrap_or("Unknown"));
}§Common Use Cases
// Building category filters for search UI
let science_groups = client.group_autocomplete(Some("science"), Some(10)).await?;
// Finding groups for dataset categorization
let energy_groups = client.group_autocomplete(Some("energy"), Some(5)).await?;Sourcepub async fn organization_autocomplete(
&self,
q: Option<&str>,
limit: Option<i32>,
) -> Result<Vec<OrganizationAutocomplete>, CkanError>
pub async fn organization_autocomplete( &self, q: Option<&str>, limit: Option<i32>, ) -> Result<Vec<OrganizationAutocomplete>, CkanError>
Get organization autocomplete suggestions