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>;