impactsense_parser/
extract.rs1use std::path::Path;
4
5use thiserror::Error;
6
7use crate::compress::CompressError;
8use crate::graph::{build_project_ir, enrich_project_ir_code_bytes, GraphPersistenceOptions};
9use crate::ir::ProjectIr;
10use crate::pipeline::ScanOptions;
11use crate::scanner::{scan_and_parse, FileScanConfig, ScannerError};
12
13pub use crate::graph::ExtractOptions;
14
15impl From<&ScanOptions> for crate::graph::ExtractOptions {
16 fn from(opts: &ScanOptions) -> Self {
17 Self {
18 verbose_imports: opts.graph.verbose_imports,
19 max_parse_warnings_per_file: opts.graph.max_parse_warnings_per_file,
20 compressor: opts.graph.compressor.clone(),
21 }
22 }
23}
24
25impl From<&GraphPersistenceOptions> for crate::graph::ExtractOptions {
26 fn from(opts: &GraphPersistenceOptions) -> Self {
27 Self {
28 verbose_imports: opts.verbose_imports,
29 max_parse_warnings_per_file: opts.max_parse_warnings_per_file,
30 compressor: opts.compressor.clone(),
31 }
32 }
33}
34
35#[derive(Debug, Error)]
36pub enum ExtractError {
37 #[error("scan/parse failed: {0}")]
38 Scanner(#[from] ScannerError),
39 #[error("code compression failed: {0}")]
40 Compress(#[from] CompressError),
41}
42
43pub fn build_project_ir_from_files(
45 root: &Path,
46 files: &[crate::scanner::ParsedFile],
47 options: &ExtractOptions,
48) -> ProjectIr {
49 build_project_ir(root, files, options)
50}
51
52pub fn scan_and_build_ir(
54 root: &Path,
55 options: &ExtractOptions,
56 scan: &ScanOptions,
57) -> Result<ProjectIr, ExtractError> {
58 let mut config = FileScanConfig::new(root);
59 config.follow_symlinks = scan.follow_symlinks;
60 config.max_file_size = scan.max_file_size;
61 let files = scan_and_parse(&config)?;
62 let mut ir = build_project_ir_from_files(root, &files, options);
63 run_ir_compression(&mut ir, root, &files, &options.compressor)?;
64 Ok(ir)
65}
66
67fn run_ir_compression(
68 ir: &mut ProjectIr,
69 root: &Path,
70 files: &[crate::scanner::ParsedFile],
71 config: &crate::graph::CompressorConfig,
72) -> Result<(), CompressError> {
73 if !config.enabled {
74 return Ok(());
75 }
76 if let Ok(handle) = tokio::runtime::Handle::try_current() {
77 return handle.block_on(enrich_project_ir_code_bytes(ir, root, files, config));
78 }
79 let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime for compression");
80 rt.block_on(enrich_project_ir_code_bytes(ir, root, files, config))
81}
82
83pub async fn scan_and_build_ir_async(
85 root: &Path,
86 options: &ExtractOptions,
87 scan: &ScanOptions,
88) -> Result<ProjectIr, ExtractError> {
89 let mut config = FileScanConfig::new(root);
90 config.follow_symlinks = scan.follow_symlinks;
91 config.max_file_size = scan.max_file_size;
92 let files = scan_and_parse(&config)?;
93 let mut ir = build_project_ir_from_files(root, &files, options);
94 enrich_project_ir_code_bytes(&mut ir, root, &files, &options.compressor).await?;
95 Ok(ir)
96}
97
98pub fn parse_files_to_ir(
100 root: &Path,
101 parse_targets: &[String],
102 options: &ExtractOptions,
103 scan: &ScanOptions,
104) -> Result<ProjectIr, ExtractError> {
105 let mut config = FileScanConfig::new(root);
106 config.follow_symlinks = scan.follow_symlinks;
107 config.max_file_size = scan.max_file_size;
108 let paths: Vec<_> = parse_targets.iter().map(std::path::PathBuf::from).collect();
109 let files = crate::scanner_incremental::scan_and_parse_incremental_vector(&config, &paths)?;
110 let mut ir = build_project_ir_from_files(root, &files, options);
111 run_ir_compression(&mut ir, root, &files, &options.compressor)?;
112 Ok(ir)
113}