#![doc = include_str!("../README.md")]
#![allow(unused_assignments)]
use miette::Diagnostic;
use thiserror::Error;
pub mod client;
pub mod mcp;
pub mod models;
pub mod problem;
pub use client::NsipClient;
pub use models::{
AnimalDetails, AnimalProfile, Breed, BreedGroup, ContactInfo, DateLastUpdated, Lineage,
LineageAnimal, Progeny, ProgenyAnimal, SearchCriteria, SearchResults, Trait, TraitRange,
TraitRangeFilter,
};
pub use problem::{CodeAction, ProblemDetails, RetryAfter};
type BoxSource = Box<dyn std::error::Error + Send + Sync + 'static>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ValidationKind {
EmptyLpnId,
InvalidBreedId,
PageRange,
EmptySearch,
CompareArity,
MissingArgument,
UnknownResource,
InvalidCursor,
UnknownTransport,
Other,
}
#[derive(Error, Debug, Diagnostic)]
#[non_exhaustive]
pub enum Error {
#[error("validation error: {message}")]
#[diagnostic(
code("nsip::cli::validation"),
url("{}/cli/validation.md", env!("NSIP_ERROR_TYPE_URI_BASE")),
help("correct the input parameters and retry")
)]
Validation {
kind: ValidationKind,
message: String,
},
#[error("API error (HTTP {status}): {message}")]
#[diagnostic(
code("nsip::api::error"),
url("{}/api/error.md", env!("NSIP_ERROR_TYPE_URI_BASE"))
)]
Api {
status: u16,
message: String,
retry_after: Option<RetryAfter>,
#[source]
source: Option<BoxSource>,
},
#[error("not found: {0}")]
#[diagnostic(
code("nsip::api::not-found"),
url("{}/api/not-found.md", env!("NSIP_ERROR_TYPE_URI_BASE")),
help("verify the identifier exists in the NSIP database")
)]
NotFound(String),
#[error("request timed out: {message}")]
#[diagnostic(
code("nsip::api::timeout"),
url("{}/api/timeout.md", env!("NSIP_ERROR_TYPE_URI_BASE")),
help("retry the request; increase the client timeout if this persists")
)]
Timeout {
message: String,
retry_after: Option<RetryAfter>,
#[source]
source: Option<BoxSource>,
},
#[error("connection error: {message}")]
#[diagnostic(
code("nsip::api::connection"),
url("{}/api/connection.md", env!("NSIP_ERROR_TYPE_URI_BASE")),
help("check network connectivity to nsipsearch.nsip.org and retry")
)]
Connection {
message: String,
retry_after: Option<RetryAfter>,
#[source]
source: Option<BoxSource>,
},
#[error("parse error: {message}")]
#[diagnostic(
code("nsip::api::upstream-parse"),
url("{}/api/upstream-parse.md", env!("NSIP_ERROR_TYPE_URI_BASE"))
)]
Parse {
message: String,
#[source]
source: Option<BoxSource>,
},
}
impl Error {
#[must_use]
pub fn validation(message: impl Into<String>) -> Self {
Self::Validation {
kind: ValidationKind::Other,
message: message.into(),
}
}
#[must_use]
pub fn validation_kind(kind: ValidationKind, message: impl Into<String>) -> Self {
Self::Validation {
kind,
message: message.into(),
}
}
#[must_use]
pub fn empty_lpn_id() -> Self {
Self::validation_kind(ValidationKind::EmptyLpnId, "lpn_id cannot be empty")
}
#[must_use]
pub fn invalid_breed_id(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::InvalidBreedId, message)
}
#[must_use]
pub fn page_range(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::PageRange, message)
}
#[must_use]
pub fn empty_search(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::EmptySearch, message)
}
#[must_use]
pub fn compare_arity(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::CompareArity, message)
}
#[must_use]
pub fn missing_argument(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::MissingArgument, message)
}
#[must_use]
pub fn unknown_resource(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::UnknownResource, message)
}
#[must_use]
pub fn invalid_cursor(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::InvalidCursor, message)
}
#[must_use]
pub fn unknown_transport(message: impl Into<String>) -> Self {
Self::validation_kind(ValidationKind::UnknownTransport, message)
}
#[must_use]
pub fn not_found(message: impl Into<String>) -> Self {
Self::NotFound(message.into())
}
#[must_use]
pub fn api(status: u16, message: impl Into<String>) -> Self {
Self::Api {
status,
message: message.into(),
retry_after: None,
source: None,
}
}
#[must_use]
pub fn timeout(message: impl Into<String>) -> Self {
Self::Timeout {
message: message.into(),
retry_after: None,
source: None,
}
}
#[must_use]
pub fn connection(message: impl Into<String>) -> Self {
Self::Connection {
message: message.into(),
retry_after: None,
source: None,
}
}
#[must_use]
pub fn parse(message: impl Into<String>) -> Self {
Self::Parse {
message: message.into(),
source: None,
}
}
}
pub type Result<T> = std::result::Result<T, Error>;