finance_query/lib.rs
1//! # finance-query
2//!
3//! A Rust library for querying financial data.
4//! Inspired by yfinance, with smart lazy loading for efficient data fetching.
5//!
6//! ## Quick Start
7//!
8//! ```no_run
9//! use finance_query::Ticker;
10//!
11//! #[tokio::main]
12//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! // Simple: Create a ticker with default configuration
14//! let ticker = Ticker::new("AAPL").await?;
15//!
16//! // First access to any quote property fetches ALL quote modules in one request
17//! if let Some(financials) = ticker.financial_data().await? {
18//! println!("Financial data: {:?}", financials);
19//! }
20//!
21//! // Subsequent accesses use cached data (no additional network calls)
22//! if let Some(profile) = ticker.asset_profile().await? {
23//! println!("Company profile: {:?}", profile);
24//! }
25//!
26//! // Chart data is fetched separately and cached by interval/range
27//! let chart = ticker.chart(
28//! finance_query::Interval::OneDay,
29//! finance_query::TimeRange::OneMonth
30//! ).await?;
31//! println!("Candles: {}", chart.candles.len());
32//!
33//! // Builder pattern: Fluent configuration
34//! let ticker_jp = Ticker::builder("7203.T")
35//! .lang("ja-JP")
36//! .region_code("JP")
37//! .timeout(std::time::Duration::from_secs(30))
38//! .build()
39//! .await?;
40//!
41//! Ok(())
42//! }
43//! ```
44//!
45//! ## Lazy Loading and Caching
46//!
47//! The library fetches on demand and caches by default:
48//! - **Quote data**: all quote modules fetched together on first property access, then reused
49//! - **Chart data**: fetched and cached per (interval, range) combination
50//! - **Recommendations**: fetched once and cached
51//!
52//! A handle caches each response for 60 seconds by default. Use `.cache(ttl)`
53//! on the builder to change that window, or `.no_cache()` to fetch fresh on
54//! every call.
55
56#![warn(missing_docs)]
57#![warn(rustdoc::missing_crate_level_docs)]
58
59// === Modules ===
60// Public modules
61/// External data source adapters (internal — use the public API modules).
62pub(crate) mod adapters;
63/// Error types and result definitions.
64pub mod error;
65/// Non-symbol-specific operations (search, lookup, screeners, market data, etc.).
66pub mod finance;
67pub mod edgar {
68 //! SEC EDGAR API client (keyless — always available, no feature flag needed).
69 //!
70 //!
71 //! Requires a one-time [`init`] call with a contact email address.
72 pub use crate::adapters::edgar::{
73 company_facts, filing_index, init, init_with_config, resolve_cik, search, submissions,
74 };
75}
76
77// Internal modules
78mod backoff;
79mod constants;
80mod models;
81#[cfg(any(feature = "risk", feature = "backtesting"))]
82mod perf_metrics;
83mod providers;
84pub(crate) mod rate_limiter;
85mod scrapers;
86mod ticker;
87mod tickers;
88mod utils;
89
90// Feature-gated external data source modules
91#[cfg(feature = "alphavantage")]
92pub mod alphavantage {
93 //! Alpha Vantage configuration (requires the `alphavantage` feature).
94 //!
95 //! Call [`init`] before constructing a provider when the API key should
96 //! come from application configuration instead of the process environment.
97 pub use crate::adapters::alphavantage::{init, init_with_timeout};
98}
99
100#[cfg(feature = "fmp")]
101pub mod fmp {
102 //! Financial Modeling Prep configuration (requires the `fmp` feature).
103 //!
104 //! Quotes, charts, fundamentals, corporate, and research data are served
105 //! through [`Providers`](crate::Providers). Only key configuration is
106 //! exposed here; the adapter's raw response types stay internal.
107 pub use crate::adapters::fmp::{init, init_with_timeout};
108}
109
110#[cfg(feature = "polygon")]
111pub mod polygon {
112 //! Massive (formerly Polygon.io) configuration (requires the `polygon`
113 //! feature).
114 //!
115 //! REST operations route through [`Providers`](crate::Providers) and
116 //! Polygon's real-time channels through
117 //! [`streaming`](crate::streaming). Only key configuration is exposed
118 //! here; the adapter's raw response types stay internal.
119 pub use crate::adapters::polygon::{init, init_with_timeout};
120}
121
122#[cfg(feature = "fred")]
123pub mod fred {
124 //! FRED economic data API (requires `fred` feature).
125 //!
126 //! Access 800k+ macroeconomic time series and US Treasury yield curve data.
127 pub use crate::adapters::fred::{
128 ReleaseDate, init, init_with_timeout, release_dates, series, treasury_yields,
129 };
130 pub use crate::models::economic::{MacroObservation, MacroSeries, TreasuryYield};
131}
132
133#[cfg(feature = "crypto")]
134pub mod crypto {
135 //! CoinGecko cryptocurrency data (requires `crypto` feature).
136 //!
137 //! Keyless shortcuts, the crypto counterpart to [`finance`](crate::finance).
138 //! [`Market::crypto_trending`](crate::domains::Market::crypto_trending) and
139 //! [`crypto_global`](crate::domains::Market::crypto_global) reach the same
140 //! data through provider routing when other CRYPTO providers are configured.
141 pub use crate::adapters::coingecko::{
142 CoinQuote, coin, coins, fetch_crypto_global_response as global,
143 fetch_crypto_trending_response as trending, fetch_symbol_search_response as search,
144 };
145 pub use crate::models::crypto::{GlobalCryptoStats, TrendingCoin};
146 pub use crate::models::discovery::reference::SymbolMatch;
147}
148
149#[cfg(feature = "gdelt")]
150pub mod gdelt {
151 //! GDELT global news search (requires `gdelt` feature, keyless).
152 //!
153 //! Keyless shortcut for callers that want GDELT specifically;
154 //! [`Ticker::news`](crate::Ticker::news) reaches the same data through
155 //! `Capability::CORPORATE` when GDELT is routed.
156 pub use crate::adapters::gdelt::fetch_news_response as news;
157 pub use crate::models::corporate::news::News;
158}
159
160#[cfg(feature = "cftc")]
161pub mod cftc {
162 //! CFTC Commitments of Traders positioning (requires `cftc` feature, keyless).
163 //!
164 //! Keyless shortcut;
165 //! [`FuturesContract::commitments_of_traders`](crate::FuturesContract::commitments_of_traders)
166 //! reaches the same data through `Capability::FUTURES` when CFTC is routed.
167 pub use crate::adapters::cftc::fetch_commitments_of_traders_response as commitments_of_traders;
168 pub use crate::models::futures::cot::{CommitmentsOfTraders, CotObservation};
169}
170
171#[cfg(feature = "openfigi")]
172pub mod openfigi {
173 //! Security-identifier resolution via OpenFIGI (requires `openfigi`
174 //! feature, keyless).
175 //!
176 //! Resolves a CUSIP, ISIN, SEDOL, or FIGI to the instruments carrying it —
177 //! the missing step for any dataset that identifies holdings by CUSIP
178 //! rather than ticker, such as 13F filings.
179 //!
180 //! Lives here rather than behind the Providers API because resolution is
181 //! not tied to a symbol handle and maps onto no
182 //! [`Capability`](crate::Capability), the same reasoning that puts
183 //! [`edgar`](crate::edgar) and [`fred`](crate::fred) at the crate root.
184 //!
185 //! No API key is required; `OPENFIGI_API_KEY` is optional and only raises
186 //! the quota.
187 //!
188 //! ```no_run
189 //! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
190 //! use finance_query::openfigi;
191 //!
192 //! // One CUSIP maps to every venue listing of the security.
193 //! for listing in openfigi::resolve_cusip("037833100").await? {
194 //! println!("{:?} on {:?}", listing.ticker, listing.exchange_code);
195 //! }
196 //! # Ok(())
197 //! # }
198 //! ```
199
200 use crate::error::Result;
201 pub use crate::models::discovery::figi::{SecurityIdKind, SecurityMapping};
202
203 /// Resolve a CUSIP to every instrument carrying it.
204 ///
205 /// Returns an empty list when the identifier is well-formed but matches
206 /// nothing; a malformed identifier is an error.
207 pub async fn resolve_cusip(cusip: &str) -> Result<Vec<SecurityMapping>> {
208 crate::adapters::openfigi::resolve(SecurityIdKind::Cusip, cusip).await
209 }
210
211 /// Resolve an ISIN to every instrument carrying it.
212 pub async fn resolve_isin(isin: &str) -> Result<Vec<SecurityMapping>> {
213 crate::adapters::openfigi::resolve(SecurityIdKind::Isin, isin).await
214 }
215
216 /// Resolve a SEDOL to every instrument carrying it.
217 pub async fn resolve_sedol(sedol: &str) -> Result<Vec<SecurityMapping>> {
218 crate::adapters::openfigi::resolve(SecurityIdKind::Sedol, sedol).await
219 }
220
221 /// Resolve an identifier of any supported [`SecurityIdKind`].
222 pub async fn resolve(kind: SecurityIdKind, id: &str) -> Result<Vec<SecurityMapping>> {
223 crate::adapters::openfigi::resolve(kind, id).await
224 }
225
226 /// Resolve many identifiers of the same kind in as few requests as
227 /// possible (OpenFIGI accepts 10 per request without a key).
228 ///
229 /// The result is positional: element `i` answers `ids[i]`, with an empty
230 /// list where nothing matched.
231 pub async fn resolve_many(
232 kind: SecurityIdKind,
233 ids: &[&str],
234 ) -> Result<Vec<Vec<SecurityMapping>>> {
235 crate::adapters::openfigi::resolve_many(kind, ids).await
236 }
237}
238
239#[cfg(feature = "defi")]
240pub mod defi {
241 //! Market-wide DeFi data via DefiLlama (requires `defi` feature, keyless).
242 //!
243 //! Chain rankings and stablecoin supplies describe the market as a whole,
244 //! not one asset, so there is no symbol handle to hang them off — they sit
245 //! at the crate root the way [`edgar`](crate::edgar) and
246 //! [`fred`](crate::fred) do.
247 //!
248 //! Protocol-shaped data *is* symbol-shaped and lives on
249 //! [`CryptoCoin::tvl`](crate::CryptoCoin::tvl) instead.
250 //!
251 //! ```no_run
252 //! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
253 //! use finance_query::defi;
254 //!
255 //! for chain in defi::chains().await?.into_iter().take(5) {
256 //! println!("{}: ${:?}", chain.name, chain.tvl);
257 //! }
258 //! # Ok(())
259 //! # }
260 //! ```
261
262 use crate::error::Result;
263 pub use crate::models::crypto::defi::{
264 ChainAllocation, ChainTvl, ProtocolTvl, StablecoinSupply, TvlPoint,
265 };
266
267 /// Fetch aggregate total value locked for every chain, largest first.
268 pub async fn chains() -> Result<Vec<ChainTvl>> {
269 crate::adapters::defillama::chains().await
270 }
271
272 /// Fetch circulating supply for every tracked stablecoin, largest first.
273 ///
274 /// Supplies are denominated in the coin's pegged asset — read `peg_type`
275 /// before summing across coins pegged to different currencies.
276 pub async fn stablecoins() -> Result<Vec<StablecoinSupply>> {
277 crate::adapters::defillama::stablecoins().await
278 }
279}
280
281pub mod feeds;
282
283#[cfg(feature = "risk")]
284pub mod risk;
285
286#[cfg(feature = "translation")]
287pub mod translation;
288
289// ============================================================================
290// High-level API - Primary interface for most use cases
291// ============================================================================
292pub mod domains;
293pub use models::quote::response::QuoteSummaryResponse;
294
295/// Yahoo `quoteSummary` module types carried by [`QuoteSummaryResponse`].
296///
297/// A [`QuoteProvider`] implementation populates these; they are also what
298/// [`Ticker::price`], [`Ticker::asset_profile`] and the other module
299/// accessors return.
300pub mod quote {
301 pub use crate::models::corporate::{
302 AssetProfile, CalendarEvents, CompanyOfficer, Earnings, EarningsHistory, EarningsTrend,
303 EquityPerformance, FundOwnership, FundPerformance, FundProfile, InsiderHolders,
304 InsiderTransactions, InstitutionOwnership, MajorHoldersBreakdown, NetSharePurchaseActivity,
305 RecommendationTrend, SecFilings, SummaryProfile, TopHoldings, UpgradeDowngradeHistory,
306 };
307 pub use crate::models::fundamentals::{DefaultKeyStatistics, FinancialData, SummaryDetail};
308 pub use crate::models::market::{IndexTrend, IndustryTrend, SectorTrend};
309 pub use crate::models::quote::price::Price;
310 pub use crate::models::quote::quote_type::QuoteTypeData;
311}
312pub use providers::CryptoProvider;
313pub use providers::EconomicProvider;
314pub use providers::ForexProvider;
315pub use providers::config::{Providers, ProvidersBuilder};
316pub use providers::{
317 CalendarProvider, ChartProvider, CommoditiesProvider, CorporateProvider, DiscoveryProvider,
318 FilingsProvider, FundamentalsProvider, FuturesProvider, IndicesProvider, MarketProvider,
319 OptionsProvider, ProviderAdapter, ProviderCore, ProviderSet, QuoteProvider, Routes,
320};
321pub use providers::{
322 Capability, CustomId, Fetch, Operation, Provider, ProviderHealth, RetryPolicy,
323};
324
325/// The attribute every capability trait implementation needs.
326///
327/// Re-exported so a downstream crate does not add its own `async-trait`
328/// dependency and risk a version mismatch with this one. The trade is that
329/// this crate's semver now covers `async-trait`: an `async-trait` 0.2 would
330/// be a breaking change here, because downstream impls use this attribute.
331pub use async_trait::async_trait;
332pub use ticker::{ClientHandle, Ticker, TickerBuilder};
333
334// Domain-specific query handles — constructable via Providers factory methods.
335pub use domains::CryptoCoin;
336pub use domains::ForexPair;
337pub use domains::{EconomicCatalog, EconomicIndicator};
338
339// Remaining Capability handles — indices, futures, commodities, filings, discovery
340pub use domains::Commodity;
341pub use domains::Discovery;
342pub use domains::Filings;
343pub use domains::FuturesContract;
344pub use domains::Index;
345pub use domains::Snapshot;
346// `Market` is unconditional — its grouped-daily/crypto methods route through
347// CHART/CRYPTO, which have their own (broader) per-method gating rather than
348// requiring fmp/polygon/alphavantage.
349pub use domains::Market;
350pub use domains::MarketCalendar;
351
352// Provider-specific financial data functions
353// (FMP, Polygon, Alpha Vantage — defined in the finance module)
354#[cfg(feature = "polygon")]
355pub use finance::symbol_sentiment;
356#[cfg(feature = "fmp")]
357pub use finance::{
358 AnalystEstimate, AnalystRecommendation, InsiderTransaction, Period, analyst_estimates,
359 analyst_recommendations, insider_trading,
360};
361#[cfg(feature = "alphavantage")]
362pub use finance::{EarningsCalendarEntry, IpoCalendarEntry, earnings_calendar, ipo_calendar};
363
364pub use tickers::{
365 BatchCapitalGainsResponse, BatchChartsResponse, BatchDividendsResponse,
366 BatchFinancialsResponse, BatchNewsResponse, BatchOptionsResponse, BatchQuotesResponse,
367 BatchRecommendationsResponse, BatchSparksResponse, BatchSplitsResponse, Tickers,
368 TickersBuilder,
369};
370
371#[cfg(feature = "indicators")]
372pub use tickers::BatchIndicatorsResponse;
373
374// ============================================================================
375// Error types and results
376// ============================================================================
377// Capability-routed response types (DISCOVERY / CALENDAR / MARKET)
378pub use models::calendar::market::{CalendarDetail, CalendarKind, MarketCalendarEntry};
379pub use models::discovery::reference::{
380 ExchangeInfo, ScreenerFilters, ScreenerMatch, SymbolDetails, SymbolMatch,
381};
382pub use models::market::performance::{
383 IndustryPe, MoverDirection, MoverQuote, SectorPe, SectorPerformance, SectorPerformanceHistory,
384};
385
386pub use error::{ErrorCategory, FinanceError, Result};
387
388// ============================================================================
389// Options - Configure API requests
390// ============================================================================
391pub use finance::{LookupOptions, LookupType, SearchOptions};
392
393// ============================================================================
394// Parameter enums - Used with Ticker and finance methods
395// ============================================================================
396pub use constants::indices::Region as IndicesRegion;
397pub use constants::screeners::Screener;
398pub use constants::sectors::Sector;
399pub use constants::{Frequency, Interval, Region, StatementType, TimeRange, ValueFormat};
400
401// ============================================================================
402// Response types - Top-level types returned by API methods
403// ============================================================================
404pub use models::{
405 calendar::{CalendarEvent, EventKind},
406 chart::Chart,
407 chart::spark::Spark,
408 corporate::earnings_transcript::EarningsTranscript,
409 corporate::governance::{EmployeeCount, ExecutiveCompensation},
410 corporate::news::News,
411 corporate::press_release::PressRelease,
412 corporate::recommendation::Recommendation,
413 corporate::transcript::{Transcript, TranscriptWithMeta},
414 discovery::lookup::LookupResults,
415 discovery::screeners::ScreenerResults,
416 discovery::search::SearchResults,
417 discovery::trending::TrendingQuote,
418 filings::{
419 CompanyFacts, CongressionalTrade, EdgarSearchResults, EdgarSubmissions, FailToDeliver,
420 FilingSearchFilters, FilingSearchHit, FilingSection, FilingSectionForm, InsiderTrade,
421 InstitutionalHolding, ProviderFiling, ProviderFilings, RiskFactor,
422 },
423 fundamentals::{
424 CompanyProfile, EarningsSurprise, EtfCountryWeighting, EtfHolding, EtfProfile,
425 EtfSectorWeighting, FinancialRatiosTtm, FinancialStatement, GradingAction, KeyMetricsTtm,
426 PriceTargetConsensus, PriceTargetSummary, RatingConsensus, ShareFloat, ShortInterest,
427 ShortVolume,
428 },
429 market::currencies::Currency,
430 market::exchanges::Exchange,
431 market::hours::MarketHours,
432 market::industries::IndustryData,
433 market::market_summary::MarketSummaryQuote,
434 market::sectors::SectorData,
435 options::Options,
436 quote::Quote,
437 sentiment::{FearAndGreed, FearGreedLabel, SymbolSentiment},
438};
439// Offline VADER sentiment scoring (feature-gated)
440#[cfg(feature = "sentiment")]
441pub use models::sentiment::{Sentiment, SentimentLabel, analyze as analyze_sentiment};
442// Multi-provider capability response types (feature-gated)
443pub use models::commodities::CommodityQuote;
444pub use models::crypto::CryptoQuote;
445pub use models::economic::{
446 EconomicCategory, EconomicRelease, EconomicSeries, EconomicSeriesMatch,
447};
448pub use models::forex::ForexQuote;
449pub use models::futures::FuturesQuote;
450#[cfg(feature = "cftc")]
451pub use models::futures::cot::{CommitmentsOfTraders, CotObservation};
452pub use models::indices::{IndexConstituent, IndexConstituentChange, IndexQuote, MajorIndex};
453pub use models::quote::snapshot::{AssetClass, MarketSnapshot};
454
455// ============================================================================
456// Nested types - Commonly accessed fields within response types
457// ============================================================================
458pub use models::{
459 chart::{Candle, CapitalGain, ChartEvents, ChartMeta, Dividend, DividendAnalytics, Split},
460 corporate::recommendation::SimilarSymbol,
461 discovery::lookup::LookupQuote,
462 discovery::screeners::ScreenerQuote,
463 discovery::search::{
464 ResearchReport, ResearchReports, SearchNews, SearchNewsList, SearchQuote, SearchQuotes,
465 },
466 filings::filing_index::{EdgarFilingIndex, EdgarFilingIndexItem},
467 filings::{
468 CikEntry, EdgarFiling, EdgarFilingFile, EdgarFilingRecent, EdgarFilings, EdgarSearchHit,
469 EdgarSearchHitsContainer, EdgarSearchSource, EdgarSearchTotal, FactConcept, FactUnit,
470 FactsByTaxonomy,
471 },
472 market::hours::MarketTime,
473 market::market_summary::SparkData,
474 options::{Contracts, OptionChain, OptionContract, OptionsQuote},
475 quote::FormattedValue,
476};
477
478// ============================================================================
479// Query builders - Types for constructing custom screener queries
480// ============================================================================
481pub use constants::exchange_codes::ExchangeCode;
482pub use constants::industries::Industry;
483pub use models::discovery::screeners::{
484 ConditionValue, EquityField, EquityScreenerQuery, FundField, FundScreenerQuery,
485 LogicalOperator, Operator, QueryCondition, QueryGroup, QueryOperand, QuoteType, ScreenerField,
486 ScreenerFieldExt, ScreenerFundCategory, ScreenerPeerGroup, ScreenerQuery, SortType,
487};
488
489// ============================================================================
490// Real-time streaming
491// ============================================================================
492// WebSocket-based real-time price streaming with a Flow-like Stream API.
493pub mod streaming;
494
495// ============================================================================
496// Format type parameters — phantom types for compile-time format selection
497// ============================================================================
498
499/// Compile-time format type parameters for [`Quote`] and other
500/// `FormattedValue`-bearing structs.
501///
502/// | Marker | `F::Value<f64>` | Access pattern |
503/// |---|---|---|
504/// | [`format::Both`] | `FormattedValue<f64>` | `.raw` / `.fmt` / `.long_fmt` |
505/// | [`format::Raw`] | `f64` | direct (no unwrapping) |
506/// | [`format::Pretty`] | `String` | human-readable string |
507///
508/// ```no_run
509/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
510/// use finance_query::{format, Ticker};
511/// let ticker = Ticker::new("AAPL").await?;
512/// let quote: finance_query::Quote<format::Raw> = ticker.quote().await?;
513/// # Ok(())
514/// # }
515/// ```
516pub mod format {
517 pub use crate::models::format::{Both, Pretty, Raw};
518}
519
520// ============================================================================
521// DataFrame support (requires "dataframe" feature)
522// ============================================================================
523// When enabled, structs with #[derive(ToDataFrame)] get a to_dataframe() method.
524// The derive macro auto-generates DataFrame conversion for all scalar fields.
525#[cfg(feature = "dataframe")]
526pub use finance_query_derive::ToDataFrame;
527
528// ============================================================================
529// Technical Indicators (requires "indicators" feature)
530// ============================================================================
531// Technical analysis indicators for price data (SMA, EMA, RSI, MACD, Bollinger Bands).
532// When enabled, Chart gets extension methods: chart.sma(), chart.ema(), chart.rsi(), etc.
533#[cfg(feature = "indicators")]
534pub mod indicators;
535
536#[cfg(feature = "indicators")]
537pub use indicators::{
538 // Summary types
539 AroonData,
540 // Individual indicator types
541 BollingerBands,
542 BollingerBandsData,
543 BullBearPowerData,
544 // Candlestick pattern types
545 CandlePattern,
546 DonchianChannelsData,
547 ElderRayData,
548 IchimokuData,
549 Indicator,
550 IndicatorError,
551 IndicatorResult,
552 IndicatorsSummary,
553 KeltnerChannelsData,
554 MacdData,
555 MacdResult,
556 PatternSentiment,
557 StochasticData,
558 SuperTrendData,
559 atr,
560 patterns,
561};
562
563// ============================================================================
564// Backtesting Engine (requires "backtesting" feature)
565// ============================================================================
566// Strategy backtesting with pre-built and custom strategies, position tracking,
567// stop-loss/take-profit, comprehensive performance metrics, parameter optimization,
568// walk-forward validation, Monte Carlo simulation, and multi-symbol portfolio.
569#[cfg(feature = "backtesting")]
570pub mod backtesting;
571
572// ============================================================================
573// Compile-time thread-safety assertions
574// ============================================================================
575// Ticker and Tickers must be Send + Sync so they can be shared across
576// async tasks and held across .await points (e.g., in Arc, tokio::spawn).
577const _: () = {
578 const fn assert_send_sync<T: Send + Sync>() {}
579 let _ = assert_send_sync::<Ticker>;
580 let _ = assert_send_sync::<Tickers>;
581};
582
583/// Parse PDF text lines from arbitrary bytes, for `fuzz/fuzz_targets`.
584///
585/// Not part of the public API. Requires the `fuzzing` feature, which no normal
586/// build enables.
587#[cfg(all(feature = "fuzzing", feature = "housetrades"))]
588#[doc(hidden)]
589pub fn __fuzz_pdf_extract_lines(bytes: Vec<u8>) -> Option<Vec<String>> {
590 crate::adapters::housetrades::pdf::extract_lines(bytes).ok()
591}
592
593/// Drive the PDF parsers that need no decryption, for `fuzz/fuzz_targets`.
594///
595/// Not part of the public API. Requires the `fuzzing` feature.
596#[cfg(all(feature = "fuzzing", feature = "housetrades"))]
597#[doc(hidden)]
598pub fn __fuzz_pdf_unencrypted(bytes: &[u8]) {
599 crate::adapters::housetrades::pdf::fuzz_unencrypted(bytes);
600}