rialo_build_lib/
detection.rs1use std::path::Path;
7
8use anyhow::{Context, Result};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ProgramType {
13 Solana,
15 RiscvC,
17 RiscvRust,
19}
20
21pub fn detect_program_type(path: &Path) -> Result<ProgramType> {
23 crate::validate_program_path(path)?;
25
26 let cargo_toml = path.join("Cargo.toml");
28
29 if cargo_toml.exists() {
30 if is_solana_program(&cargo_toml)? {
32 return Ok(ProgramType::Solana);
33 } else {
34 return Ok(ProgramType::RiscvRust);
36 }
37 }
38
39 if has_c_sources(path)? {
41 return Ok(ProgramType::RiscvC);
42 }
43
44 Err(anyhow::anyhow!(
45 "Could not detect program type in {}. Directory should contain:\n\
46 - Cargo.toml (for Solana or RISC-V Rust programs)\n\
47 - C source files (.c or .S) (for RISC-V C programs)",
48 path.display()
49 ))
50}
51
52fn is_solana_program(cargo_toml_path: &Path) -> Result<bool> {
54 let manifest = cargo_toml::Manifest::from_path(cargo_toml_path).with_context(|| {
55 format!(
56 "Failed to parse Cargo.toml at {}",
57 cargo_toml_path.display()
58 )
59 })?;
60
61 let solana_deps = [
63 "solana-program",
64 "solana-sdk",
65 "rialo-s-program",
66 "rialo-s-sdk",
67 ];
68
69 for dep_name in solana_deps {
71 if manifest.dependencies.contains_key(dep_name) {
72 return Ok(true);
73 }
74 }
75
76 for dep_name in solana_deps {
78 if manifest.dev_dependencies.contains_key(dep_name) {
79 return Ok(true);
80 }
81 }
82
83 Ok(false)
84}
85
86fn has_c_sources(path: &Path) -> Result<bool> {
88 if has_c_files_in_dir(path)? {
90 return Ok(true);
91 }
92
93 let src_dir = path.join("src");
95 if src_dir.exists() && has_c_files_in_dir(&src_dir)? {
96 return Ok(true);
97 }
98
99 Ok(false)
100}
101
102fn has_c_files_in_dir(dir: &Path) -> Result<bool> {
104 if !dir.is_dir() {
105 return Ok(false);
106 }
107
108 let entries = std::fs::read_dir(dir)
109 .with_context(|| format!("Failed to read directory {}", dir.display()))?;
110
111 for entry in entries.flatten() {
112 let path = entry.path();
113 if path.is_file() {
114 if let Some(ext) = path.extension() {
115 if ext == "c" || ext == "S" {
116 return Ok(true);
117 }
118 }
119 }
120 }
121
122 Ok(false)
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_program_type_equality() {
131 assert_eq!(ProgramType::Solana, ProgramType::Solana);
132 assert_ne!(ProgramType::Solana, ProgramType::RiscvC);
133 assert_ne!(ProgramType::RiscvC, ProgramType::RiscvRust);
134 }
135}