baobao_codegen/schema/
computed.rs

1//! Computed data from IR analysis.
2//!
3//! This module provides [`ComputedData`], a struct that holds pre-computed
4//! analysis results from the Application IR. This avoids repeated computation
5//! across different parts of the code generation pipeline.
6
7use std::collections::HashSet;
8
9use baobao_ir::{AppIR, ContextFieldInfo};
10use serde::Serialize;
11
12/// Pre-computed data from IR analysis.
13///
14/// This struct aggregates commonly-needed analysis results that would otherwise
15/// be computed multiple times by different generators and phases.
16#[derive(Debug, Default, Clone, Serialize)]
17pub struct ComputedData {
18    /// Context fields (database pools, HTTP clients, etc.)
19    pub context_fields: Vec<ContextFieldInfo>,
20    /// All command handler paths (for orphan detection)
21    pub command_paths: HashSet<String>,
22    /// Whether any resources require async initialization
23    pub is_async: bool,
24    /// Whether the application has database resources
25    pub has_database: bool,
26    /// Whether the application has HTTP client resources
27    pub has_http: bool,
28    /// Number of top-level commands
29    pub command_count: usize,
30    /// Total number of leaf commands (handlers)
31    pub handler_count: usize,
32}
33
34impl ComputedData {
35    /// Compute all data from an Application IR.
36    pub fn from_ir(ir: &AppIR) -> Self {
37        let context_fields = ir.context_fields();
38        let command_paths: HashSet<String> = ir.handler_paths().into_iter().collect();
39        let handler_count = command_paths.len();
40
41        Self {
42            context_fields,
43            command_paths,
44            is_async: ir.has_async(),
45            has_database: ir.has_database(),
46            has_http: ir.has_http(),
47            command_count: ir.commands().count(),
48            handler_count,
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use baobao_ir::{AppMeta, DatabaseResource, DatabaseType, PoolConfig, Resource};
56
57    use super::*;
58
59    fn make_test_ir() -> AppIR {
60        AppIR {
61            meta: AppMeta {
62                name: "test".into(),
63                version: "1.0.0".into(),
64                description: None,
65                author: None,
66            },
67            resources: vec![Resource::Database(DatabaseResource {
68                name: "db".into(),
69                db_type: DatabaseType::Postgres,
70                env_var: "DATABASE_URL".into(),
71                pool: PoolConfig::default(),
72                sqlite: None,
73            })],
74            operations: vec![],
75        }
76    }
77
78    #[test]
79    fn test_computed_data_from_ir() {
80        let ir = make_test_ir();
81        let computed = ComputedData::from_ir(&ir);
82
83        assert!(computed.is_async);
84        assert!(computed.has_database);
85        assert!(!computed.has_http);
86        assert_eq!(computed.context_fields.len(), 1);
87    }
88
89    #[test]
90    fn test_computed_data_default() {
91        let computed = ComputedData::default();
92
93        assert!(!computed.is_async);
94        assert!(!computed.has_database);
95        assert!(!computed.has_http);
96        assert!(computed.context_fields.is_empty());
97    }
98}