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