llmcc/
lib.rs

1use std::error::Error;
2use std::path::Path;
3
4use llmcc_core::*;
5
6/// Input options for building an LLMCC project.
7pub struct LlmccOptions {
8    pub files: Vec<String>,
9    pub dir: Option<String>,
10    pub print_ir: bool,
11    pub print_block: bool,
12    pub project_graph: bool,
13    pub pagerank: bool,
14    pub top_k: Option<usize>,
15    pub pagerank_direction: String,
16    pub pagerank_iterations: usize,
17    pub query: Option<String>,
18    pub recursive: bool,
19    pub dependents: bool,
20}
21
22pub fn run_main<L: LanguageTrait>(opts: &LlmccOptions) -> Result<Option<String>, Box<dyn Error>> {
23    let (cc, files) = if let Some(dir) = opts.dir.as_ref() {
24        let ctx = CompileCtxt::from_dir::<_, L>(Path::new(dir))?;
25        let file_paths = ctx.get_files();
26        (ctx, file_paths)
27    } else {
28        let ctx = CompileCtxt::from_files::<L>(&opts.files)?;
29        (ctx, opts.files.clone())
30    };
31
32    let use_compact_builder = opts.project_graph && opts.query.is_none();
33    build_llmcc_ir_with_config::<L>(&cc, IrBuildConfig::default())?;
34    let globals = cc.create_globals();
35
36    if opts.print_ir {
37        for (index, _) in files.iter().enumerate() {
38            let unit = cc.compile_unit(index);
39            print_llmcc_ir(unit);
40        }
41    }
42
43    for (index, _) in files.iter().enumerate() {
44        let unit = cc.compile_unit(index);
45        L::collect_symbols(unit, globals);
46    }
47
48    let mut pg = ProjectGraph::new(&cc);
49    // Keep the full graph when a query is requested so dependency queries remain accurate.
50    let graph_config = if use_compact_builder {
51        GraphBuildConfig::compact()
52    } else {
53        GraphBuildConfig::default()
54    };
55    for (index, _) in files.iter().enumerate() {
56        let unit = cc.compile_unit(index);
57        L::bind_symbols(unit, globals);
58        let unit_graph = build_llmcc_graph_with_config::<L>(unit, index, graph_config)?;
59
60        if opts.print_block {
61            print_llmcc_graph(unit_graph.root(), unit);
62        }
63
64        pg.add_child(unit_graph);
65    }
66
67    pg.link_units();
68
69    if opts.recursive && opts.dependents {
70        return Err("`--recursive` is not supported together with `--dependents`".into());
71    }
72
73    let mut outputs = Vec::new();
74
75    if opts.project_graph {
76        if opts.pagerank {
77            let limit = Some(opts.top_k.unwrap_or(25));
78            pg.set_compact_rank_limit(limit);
79
80            // Configure PageRank direction based on CLI option
81            let direction = match opts.pagerank_direction.as_str() {
82                "depends-on" => llmcc_core::PageRankDirection::DependsOn,
83                _ => llmcc_core::PageRankDirection::DependedBy, // default
84            };
85            pg.set_pagerank_direction(direction);
86        }
87        outputs.push(pg.render_compact_graph());
88    } else {
89        if let Some(name) = opts.query.as_ref() {
90            let query = ProjectQuery::new(&pg);
91            let query_output = if opts.dependents {
92                query.find_depended(name).format_for_llm()
93            } else if opts.recursive {
94                query.find_depends_recursive(name).format_for_llm()
95            } else {
96                query.find_depends(name).format_for_llm()
97            };
98            outputs.push(query_output);
99        }
100    }
101
102    if outputs.is_empty() {
103        Ok(None)
104    } else {
105        Ok(Some(outputs.join("\n")))
106    }
107}