1pub mod cli;
2pub mod defaults;
3pub mod error;
4pub mod filter;
5pub mod git;
6pub mod highlight;
7pub mod pdf;
8pub mod types;
9
10use std::path::Path;
11
12use crate::error::Error;
13use crate::types::Config;
14
15pub fn run(config: &Config) -> Result<(), Error> {
18 let repo_path = git::verify_repo(&config.repo_path)?;
19 let mut metadata = git::get_metadata(&repo_path, config)?;
20
21 let all_paths = git::list_tracked_files(&repo_path, config)?;
22 let file_filter = filter::FileFilter::new(&config.include_patterns, &config.exclude_patterns)?;
23 let mut paths: Vec<_> = file_filter.filter_paths(all_paths).collect();
24 paths.sort();
25
26 let highlighter = highlight::Highlighter::new(&config.theme)?;
27 let mut doc = pdf::create_document(config)?;
28
29 let line_counts: Vec<usize> = paths
32 .iter()
33 .map(|p| count_lines(&repo_path, p, config))
34 .collect();
35
36 let toc_entries: Vec<(&Path, usize)> = paths
37 .iter()
38 .zip(&line_counts)
39 .map(|(p, &n)| (p.as_path(), n))
40 .collect();
41
42 metadata.file_count = toc_entries.len();
43 metadata.total_lines = line_counts.iter().sum();
44
45 pdf::cover::render(&mut doc, &metadata);
47
48 if config.toc {
49 pdf::toc::render(&mut doc, &toc_entries);
50 }
51 if config.file_tree {
52 pdf::tree::render(&mut doc, &paths);
53 }
54
55 paths.iter().zip(&line_counts).for_each(|(path, &lines)| {
58 if let Some(content) = read_text_file(&repo_path, path, config) {
59 let highlighted = highlighter.highlight_lines(&content, path);
60 let display_path = path.display().to_string();
61 pdf::code::render_file(&mut doc, &display_path, highlighted, lines, !config.no_line_numbers, config.font_size as u8);
62 }
64 });
65
66 pdf::write_pdf(doc, &config.output_path)?;
67
68 eprintln!(
69 "wrote {} files ({} lines) to {}",
70 metadata.file_count,
71 metadata.total_lines,
72 config.output_path.display()
73 );
74
75 Ok(())
76}
77
78fn read_text_file(repo_path: &Path, path: &Path, config: &Config) -> Option<String> {
80 git::read_file_content(repo_path, path, config)
81 .ok()
82 .filter(|c| !filter::is_binary(c.as_bytes()))
83 .filter(|c| !filter::is_minified(c))
84}
85
86fn count_lines(repo_path: &Path, path: &Path, config: &Config) -> usize {
88 read_text_file(repo_path, path, config)
89 .map(|c| c.lines().count())
90 .unwrap_or(0)
91}