python_proto_importer/verification/
package_structure.rs1use anyhow::Result;
2use std::path::{Path, PathBuf};
3
4pub fn determine_package_structure_legacy(out_abs: &Path) -> Result<(PathBuf, String)> {
7 let out_name = out_abs
8 .file_name()
9 .and_then(|n| n.to_str())
10 .unwrap_or("generated");
11
12 tracing::debug!(
13 "determine_package_structure_legacy: using simple structure: PYTHONPATH={}, package_name={}",
14 out_abs
15 .parent()
16 .map(|p| p.display().to_string())
17 .unwrap_or_else(|| "<none>".to_string()),
18 out_name
19 );
20
21 let parent = out_abs.parent();
22 if let Some(parent_dir) = parent.filter(|p| p.exists()) {
23 return Ok((parent_dir.to_path_buf(), out_name.to_string()));
24 }
25
26 Ok((out_abs.to_path_buf(), String::new()))
27}
28
29pub fn determine_package_structure(out_abs: &Path) -> Result<(PathBuf, String)> {
35 let out_name = out_abs
36 .file_name()
37 .and_then(|n| n.to_str())
38 .unwrap_or("generated");
39
40 tracing::debug!(
41 "determine_package_structure: analyzing out_abs={}",
42 out_abs.display()
43 );
44 tracing::debug!("determine_package_structure: out_name={}", out_name);
45
46 if let Some(parent_dir) = out_abs.parent() {
47 tracing::debug!(
48 "determine_package_structure: parent_dir={}",
49 parent_dir.display()
50 );
51 if parent_dir.exists() {
52 let parent_init = parent_dir.join("__init__.py");
53 tracing::debug!(
54 "determine_package_structure: checking for parent_init={}",
55 parent_init.display()
56 );
57 if parent_init.exists() {
58 tracing::debug!(
59 "determine_package_structure: parent is a package (has __init__.py)"
60 );
61 if let Some(grand) = parent_dir.parent() {
62 tracing::debug!(
63 "determine_package_structure: grandparent_dir={}",
64 grand.display()
65 );
66 if grand.exists() {
67 let parent_name = parent_dir
68 .file_name()
69 .and_then(|n| n.to_str())
70 .unwrap_or("");
71 let pkg = if parent_name.is_empty() {
72 out_name.to_string()
73 } else {
74 format!("{}.{}", parent_name, out_name)
75 };
76 tracing::debug!(
77 "determine_package_structure: using nested package structure: PYTHONPATH={}, package_name={}",
78 grand.display(),
79 pkg
80 );
81 return Ok((grand.to_path_buf(), pkg));
82 } else {
83 tracing::debug!(
84 "determine_package_structure: grandparent does not exist, falling back to standard structure"
85 );
86 }
87 } else {
88 tracing::debug!(
89 "determine_package_structure: no grandparent, falling back to standard structure"
90 );
91 }
92 } else {
93 tracing::debug!(
94 "determine_package_structure: parent is not a package (no __init__.py)"
95 );
96 }
97 tracing::debug!(
98 "determine_package_structure: using standard structure: PYTHONPATH={}, package_name={}",
99 parent_dir.display(),
100 out_name
101 );
102 return Ok((parent_dir.to_path_buf(), out_name.to_string()));
103 } else {
104 tracing::debug!("determine_package_structure: parent directory does not exist");
105 }
106 } else {
107 tracing::debug!("determine_package_structure: no parent directory");
108 }
109
110 tracing::debug!(
111 "determine_package_structure: fallback to out_abs as PYTHONPATH: PYTHONPATH={}, package_name=empty",
112 out_abs.display()
113 );
114 Ok((out_abs.to_path_buf(), String::new()))
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use std::fs;
121 use std::io;
122 use tempfile::TempDir;
123
124 #[test]
125 fn test_determine_package_structure_legacy_with_parent() -> io::Result<()> {
126 let temp_dir = TempDir::new()?;
127 let parent_path = temp_dir.path();
128 let out_path = parent_path.join("my_package");
129 fs::create_dir(&out_path)?;
130
131 let result = determine_package_structure_legacy(&out_path).unwrap();
132
133 assert_eq!(result.0, parent_path);
134 assert_eq!(result.1, "my_package");
135
136 Ok(())
137 }
138
139 #[test]
140 fn test_determine_package_structure_legacy_no_parent() {
141 let root_path = PathBuf::from("/");
142 let result = determine_package_structure_legacy(&root_path).unwrap();
143
144 assert_eq!(result.0, root_path);
146 assert_eq!(result.1, String::new());
147 }
148
149 #[test]
150 fn test_determine_package_structure_simple_case() -> io::Result<()> {
151 let temp_dir = TempDir::new()?;
152 let parent_path = temp_dir.path();
153 let out_path = parent_path.join("simple_package");
154 fs::create_dir(&out_path)?;
155
156 let result = determine_package_structure(&out_path).unwrap();
157
158 assert_eq!(result.0, parent_path);
160 assert_eq!(result.1, "simple_package");
161
162 Ok(())
163 }
164
165 #[test]
166 fn test_determine_package_structure_nested_package() -> io::Result<()> {
167 let temp_dir = TempDir::new()?;
168 let grandparent_path = temp_dir.path();
169 let parent_path = grandparent_path.join("parent_pkg");
170 let out_path = parent_path.join("child_pkg");
171
172 fs::create_dir_all(&out_path)?;
173 fs::write(parent_path.join("__init__.py"), "")?; let result = determine_package_structure(&out_path).unwrap();
176
177 assert_eq!(result.0, grandparent_path);
179 assert_eq!(result.1, "parent_pkg.child_pkg");
180
181 Ok(())
182 }
183
184 #[test]
185 fn test_determine_package_structure_nested_package_no_grandparent() -> io::Result<()> {
186 let temp_dir = TempDir::new()?;
187 let parent_path = temp_dir.path().join("parent_pkg");
188 let out_path = parent_path.join("child_pkg");
189
190 fs::create_dir_all(&out_path)?;
191 fs::write(parent_path.join("__init__.py"), "")?;
192
193 let result = determine_package_structure(&out_path).unwrap();
194
195 assert_eq!(result.0, temp_dir.path());
197 assert_eq!(result.1, "parent_pkg.child_pkg");
198
199 Ok(())
200 }
201
202 #[test]
203 fn test_determine_package_structure_fallback_to_self() {
204 let nonexistent_path = PathBuf::from("/nonexistent/path/package");
205
206 let result = determine_package_structure(&nonexistent_path).unwrap();
207
208 assert_eq!(result.0, nonexistent_path);
210 assert_eq!(result.1, String::new());
211 }
212
213 #[test]
214 fn test_determine_package_structure_empty_parent_name() -> io::Result<()> {
215 let temp_dir = TempDir::new()?;
216 let parent_path = temp_dir.path().join("");
217 let out_path = parent_path.join("package");
218
219 let result = determine_package_structure(&out_path);
221
222 assert!(result.is_ok());
224
225 Ok(())
226 }
227}