Skip to main content

keyhog_sources/
lib.rs

1//! Pluggable input sources for KeyHog.
2//!
3//! Each source implements the [`keyhog_core::Source`] trait and yields [`keyhog_core::Chunk`]
4//! values for the scanner. Sources are gated behind cargo features so only the
5//! transitive dependencies you actually need are compiled.
6
7#![allow(clippy::too_many_arguments)]
8
9mod timeouts;
10
11/// Local HTTP compatibility shim backed by reqwest. Only present when
12/// at least one feature that pulls in `reqwest` is enabled —
13/// otherwise this module would `pub use reqwest::*` against a crate
14/// that wasn't compiled in, which fails resolution on stable rustc
15/// (especially on Windows where `--no-default-features` is the
16/// release profile we ship for the no-Hyperscan build).
17#[cfg(any(feature = "web", feature = "github", feature = "slack", feature = "s3"))]
18pub mod reqwest {
19    pub use ::reqwest::*;
20}
21
22#[cfg(feature = "binary")]
23mod binary;
24#[cfg(feature = "docker")]
25mod docker;
26mod filesystem;
27#[cfg(feature = "git")]
28mod git;
29#[cfg(feature = "github")]
30mod github_org;
31#[cfg(feature = "s3")]
32mod s3;
33#[cfg(feature = "slack")]
34mod slack;
35mod stdin;
36pub mod strings;
37#[cfg(feature = "web")]
38mod web;
39
40#[cfg(feature = "binary")]
41pub use binary::BinarySource;
42#[cfg(feature = "docker")]
43pub use docker::DockerImageSource;
44pub use filesystem::FilesystemSource;
45#[cfg(feature = "git")]
46pub use git::GitDiffSource;
47#[cfg(feature = "git")]
48pub use git::GitHistorySource;
49#[cfg(feature = "git")]
50pub use git::GitSource;
51#[cfg(feature = "github")]
52pub use github_org::GitHubOrgSource;
53#[cfg(feature = "s3")]
54pub use s3::S3Source;
55#[cfg(feature = "slack")]
56pub use slack::SlackSource;
57pub use stdin::StdinSource;
58#[cfg(feature = "web")]
59pub use web::WebSource;
60
61use keyhog_core::registry::get_source_registry;
62// Arc is used by the registry-registration plugins below. The cfg
63// match has to track the *actual* `Arc::new(...)` call sites, not
64// every feature flag the file references — gating broader than this
65// triggers `unused_imports` on builds that include only Docker
66// (which doesn't go through the registry).
67#[cfg(any(feature = "slack", feature = "s3"))]
68use std::sync::Arc;
69
70/// Create a source instance from a name and optional parameters.
71/// This allows the CLI to remain agnostic of specific source implementations.
72pub fn create_source(
73    name: &str,
74    params: Option<&str>,
75) -> Result<Box<dyn keyhog_core::Source>, keyhog_core::SourceError> {
76    match name {
77        "slack" => {
78            if let Some(token) = params {
79                #[cfg(feature = "slack")]
80                return Ok(Box::new(SlackSource::new(token)));
81                #[cfg(not(feature = "slack"))]
82                {
83                    let _ = token;
84                    return Err(keyhog_core::SourceError::Other(
85                        "slack feature not enabled".into(),
86                    ));
87                }
88            }
89            Err(keyhog_core::SourceError::Other(
90                "slack source requires a token: slack:TOKEN".into(),
91            ))
92        }
93        "docker" => {
94            if let Some(image) = params {
95                #[cfg(feature = "docker")]
96                return Ok(Box::new(DockerImageSource::new(image)));
97                #[cfg(not(feature = "docker"))]
98                {
99                    let _ = image;
100                    return Err(keyhog_core::SourceError::Other(
101                        "docker feature not enabled".into(),
102                    ));
103                }
104            }
105            Err(keyhog_core::SourceError::Other(
106                "docker source requires an image name: docker:IMAGE".into(),
107            ))
108        }
109        "s3" => {
110            if let Some(bucket) = params {
111                #[cfg(feature = "s3")]
112                return Ok(Box::new(S3Source::new(bucket)));
113                #[cfg(not(feature = "s3"))]
114                {
115                    let _ = bucket;
116                    return Err(keyhog_core::SourceError::Other(
117                        "s3 feature not enabled".into(),
118                    ));
119                }
120            }
121            Err(keyhog_core::SourceError::Other(
122                "s3 source requires a bucket name: s3:BUCKET".into(),
123            ))
124        }
125        _ => Err(keyhog_core::SourceError::Other(format!(
126            "unknown source plugin: {}",
127            name
128        ))),
129    }
130}
131
132/// Register all compiled-in source plugins into the global registry.
133/// This allows the CLI to discover sources like `slack` or `s3` via the
134/// generic `--source` flag without hardcoded logic in the main crate.
135pub fn register_plugins() {
136    #[allow(unused_variables)]
137    let registry = get_source_registry();
138
139    #[cfg(feature = "slack")]
140    if let Ok(token) = std::env::var("SLACK_TOKEN") {
141        registry.register(Arc::new(SlackSource::new(token)));
142    }
143
144    #[cfg(feature = "s3")]
145    if let Ok(bucket) = std::env::var("S3_BUCKET") {
146        registry.register(Arc::new(S3Source::new(bucket)));
147    }
148}