Skip to main content

patent/
lib.rs

1//! `patent` — a prior-art search for your code ideas.
2//!
3//! Takes a plain-English dev-tool idea and searches the open-source ecosystem —
4//! crates.io, npm, PyPI, GitHub, Go, Maven, NuGet, RubyGems, Docker Hub,
5//! Homebrew, Packagist, Hex, Artifact Hub, AUR, Hackage, Nixpkgs, the VS Code
6//! Marketplace, the JetBrains Marketplace, and Hacker News — for prior art,
7//! then gives an honest, scoped verdict on whether it's already been built.
8//! The exact set searched is chosen per query; whichever sources actually
9//! responded are always surfaced.
10//!
11//! **Integrity principle:** this tool can prove something *exists*, but never
12//! that it *doesn't* — it only searched some sources. All output is scoped to
13//! "what was found in the sources checked."
14//!
15//! # Install
16//!
17//! ```bash
18//! cargo install patent
19//! ```
20//!
21//! # Usage
22//!
23//! ```bash
24//! patent "interactive cli to kill whatever's on a port"   # interactive TUI
25//! patent "react component for infinite scroll" --json      # structured output
26//! patent "kubernetes log viewer" --fast                    # skip the LLM verdict
27//! patent "vector database" --api-base https://api.openai.com/v1 --model gpt-4o-mini
28//! ```
29//!
30//! # Using the library
31//!
32//! `patent` is primarily the engine behind the CLI of the same name, but the
33//! core is reusable: [`sources::search_all`] fans out to the registries,
34//! [`rank`] (or the async-safe [`rank::rank_async`]) orders matches by
35//! semantic similarity, and [`verdict::assess`]
36//! turns them into an integrity-scoped [`Verdict`] via any [`Llm`] backend
37//! (local Ollama or an OpenAI-compatible API). [`Match::last_updated`] carries
38//! the raw normalised timestamp; [`freshness::age`] turns it into the
39//! human label and staleness flag the TUI renders.
40
41pub mod freshness;
42pub mod llm;
43pub mod model;
44pub mod ollama;
45pub mod openai;
46pub mod rank;
47pub mod sources;
48#[doc(hidden)]
49pub mod tui;
50pub mod verdict;
51
52pub use llm::Llm;
53pub use model::{Match, Query, Saturation, Source, Verdict};
54
55/// Library-level error type. The binary maps these to `anyhow` with context.
56#[derive(Debug, thiserror::Error)]
57pub enum Error {
58    #[error("http request failed: {0}")]
59    Http(#[from] reqwest::Error),
60
61    /// The shared HTTP client could not be constructed (e.g. the TLS backend
62    /// failed to initialize). Surfaced instead of panicking so library
63    /// consumers can handle it. Distinct from [`Error::Http`] (a request that
64    /// was sent and failed) — this is a failure to build the client at all.
65    #[error("failed to build HTTP client: {0}")]
66    HttpClient(#[source] reqwest::Error),
67
68    #[error("failed to parse response: {0}")]
69    Parse(String),
70
71    /// A source's search surface is genuinely, persistently unavailable — not a
72    /// transient blip but a wall a retry cannot get past (e.g. PyPI's search page
73    /// is bot-walled to non-browser clients and it has no keyless search API).
74    /// Carries accurate, user-facing wording. Distinct from [`Error::Http`] /
75    /// [`Error::Parse`] so the fan-out can skip the retry it would waste hitting
76    /// the same wall.
77    #[error("{0}")]
78    Unavailable(String),
79
80    /// LLM endpoint could not be reached. The message carries the address and a hint.
81    #[error("{0}")]
82    LlmUnreachable(String),
83
84    /// LLM endpoint was reached but rejected the request (unknown model, bad key,
85    /// server error). The message carries the reason and a hint.
86    #[error("{0}")]
87    LlmRejected(String),
88
89    #[error("embedding failed: {0}")]
90    Embedding(String),
91}
92
93/// Crate result alias.
94pub type Result<T> = std::result::Result<T, Error>;