1#![cfg_attr(not(tarpaulin), deny(warnings))]
2#![cfg_attr(tarpaulin, allow(warnings))]
3
4pub mod error;
37pub mod file;
38pub mod types;
39pub mod config;
40pub mod traits;
41pub mod utils;
42
43#[cfg(feature = "scaling")]
45pub mod scaling;
46
47pub use error::{ScribeError, Result};
49
50pub use file::{
51 FileInfo, FileType, Language, RenderDecision, RenderDecisionCategory,
52 DocumentationFormat, ConfigurationFormat, GitStatus, GitFileStatus,
53 bytes_to_human, BINARY_EXTENSIONS, MARKDOWN_EXTENSIONS,
54};
55
56pub use types::{
57 Position, Range,
59
60 ScoreComponents, HeuristicWeights,
62
63 RepositoryInfo, SizeStatistics, LanguageStats, FileTypeStats,
65 GitStatistics, ChurnInfo,
66
67 CentralityScores, GraphStats,
69
70 AnalysisResult, AnalysisMetadata,
72};
73
74pub use config::{
75 Config, GeneralConfig, FilteringConfig, AnalysisConfig, ScoringConfig,
76 PerformanceConfig, GitConfig, FeatureFlags, OutputConfig,
77 CustomScoringRule, ScoreModifier, OutputFormat,
78};
79
80pub use traits::{
81 FileAnalyzer, HeuristicScorer, RepositoryAnalyzer, GitIntegration,
83 CentralityComputer, LanguageExtension,
84
85 PatternMatcher, OutputFormatter, CacheStorage, ProgressReporter,
87 PluginRegistry,
88
89 DependencyGraph, DependencyNodeMetadata, GitRepositoryInfo,
91 DocumentationBlock, DocumentationType, CacheStats,
92};
93
94pub use utils::{
96 path::{normalize_path, relative_path, is_under_directory, path_depth, is_hidden, find_repo_root},
97 string::{truncate, dedent, count_lines, is_likely_binary, extract_identifier},
98 time::{duration_to_human, current_timestamp},
99 math::{mean, median, std_deviation, normalize, clamp},
100 validation::{validate_readable_path, validate_directory, validate_file},
101 hash::{generate_hash, hash_file_content},
102};
103
104pub const VERSION: &str = env!("CARGO_PKG_VERSION");
106
107pub mod meta {
109 pub const NAME: &str = env!("CARGO_PKG_NAME");
111
112 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
114
115 pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
117
118 pub const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
120
121 pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
123
124 pub const LICENSE: &str = env!("CARGO_PKG_LICENSE");
126
127 pub const TARGET: Option<&str> = option_env!("TARGET");
129
130 pub const BUILD_TIMESTAMP: Option<&str> = option_env!("BUILD_TIMESTAMP");
132
133 pub const GIT_HASH: Option<&str> = option_env!("GIT_HASH");
135
136 pub fn info() -> String {
138 format!(
139 "{} v{} - {}",
140 NAME,
141 VERSION,
142 DESCRIPTION
143 )
144 }
145
146 pub fn build_info() -> String {
148 let mut info = format!("{} v{}", NAME, VERSION);
149
150 if let Some(target) = TARGET {
151 info.push_str(&format!(" ({})", target));
152 }
153
154 if let Some(git_hash) = GIT_HASH {
155 info.push_str(&format!(" [{}]", &git_hash[..8]));
156 }
157
158 if let Some(timestamp) = BUILD_TIMESTAMP {
159 info.push_str(&format!(" built on {}", timestamp));
160 }
161
162 info
163 }
164}
165
166pub mod prelude {
168 pub use crate::{
171 Result, ScribeError,
173 FileInfo, FileType, Language, RenderDecision,
174 ScoreComponents, HeuristicWeights,
175 Config, FeatureFlags,
176
177 FileAnalyzer, HeuristicScorer, RepositoryAnalyzer,
179 OutputFormatter, ProgressReporter,
180
181 normalize_path, duration_to_human, generate_hash,
183 };
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_version() {
192 assert!(!VERSION.is_empty());
193 assert!(VERSION.parse::<semver::Version>().is_ok());
194 }
195
196 #[test]
197 fn test_meta_info() {
198 let info = meta::info();
199 assert!(info.contains("scribe-core"));
200 assert!(info.contains(VERSION));
201 }
202
203 #[test]
204 fn test_build_info() {
205 let build_info = meta::build_info();
206 assert!(build_info.contains("scribe-core"));
207 assert!(build_info.contains(VERSION));
208 }
209
210 #[test]
211 fn test_prelude_imports() {
212 use crate::prelude::*;
214
215 let config = Config::default();
216 assert!(config.general.verbosity <= 4);
217
218 let decision = RenderDecision::include("test");
219 assert!(decision.should_include());
220
221 let hash = generate_hash(&"test");
222 assert!(!hash.is_empty());
223 }
224
225 #[test]
226 fn test_core_functionality() {
227 let err = ScribeError::config("test error");
229 assert!(err.to_string().contains("test error"));
230
231 let lang = Language::from_extension("rs");
233 assert_eq!(lang, Language::Rust);
234
235 let depth = path_depth("src/lib/mod.rs");
237 assert_eq!(depth, 3);
238
239 let truncated = truncate("hello world", 5);
241 assert_eq!(truncated, "he...");
242 }
243
244 #[test]
245 fn test_score_components() {
246 let mut scores = ScoreComponents::zero();
247 scores.doc_score = 0.8;
248 scores.import_score = 0.6;
249
250 let weights = HeuristicWeights::default();
251 scores.compute_final_score(&weights);
252
253 assert!(scores.final_score > 0.0);
254 assert!(scores.final_score <= 1.0);
255 }
256
257 #[test]
258 fn test_configuration() {
259 let config = Config::default();
260 assert!(config.validate().is_ok());
261
262 let hash1 = config.compute_hash();
263 let hash2 = config.compute_hash();
264 assert_eq!(hash1, hash2);
265 }
266}