33#include < algorithm>
44#include < regex>
55#include < sstream>
6+ #include < fstream>
67
78// PackageId implementation
89
@@ -124,33 +125,56 @@ bool PackageRegistry::load_package(const std::filesystem::path &path)
124125 // Parse the WIT file
125126 auto parse_result = WitGrammarParser::parse (path.string ());
126127
127- // Skip if no package name defined (these are interface-only files that will be
128- // loaded as part of their parent package)
128+ // If no package name defined, try to infer it from directory structure or sibling files
129+ PackageId package_id_val;
129130 if (parse_result.packageName .empty ())
130131 {
131- return true ; // Not an error - just skip interface-only files
132+ auto inferred_id = infer_package_from_path (path);
133+ if (!inferred_id)
134+ {
135+ // Can't infer package - skip this file
136+ return true ; // Not an error, just skip
137+ }
138+ package_id_val = *inferred_id;
132139 }
133-
134- // Parse package ID from package name
135- auto package_id = PackageId::parse (parse_result.packageName );
136- if (!package_id)
140+ else
137141 {
138- return false ;
142+ // Parse package ID from package name
143+ auto package_id = PackageId::parse (parse_result.packageName );
144+ if (!package_id)
145+ {
146+ return false ;
147+ }
148+ package_id_val = *package_id;
139149 }
140150
141- // Create package
142- auto package = std::make_unique<WitPackage>();
143- package->id = *package_id;
144- package->source_path = path;
151+ // Check if package already exists in registry
152+ auto *existing_package = get_package (package_id_val);
145153
146- // Add all interfaces
147- for (const auto &interface : parse_result.interfaces )
154+ if (existing_package)
148155 {
149- package->add_interface (interface);
156+ // Package already exists - merge interfaces into it
157+ for (const auto &interface : parse_result.interfaces )
158+ {
159+ existing_package->add_interface (interface);
160+ }
161+ }
162+ else
163+ {
164+ // Create new package
165+ auto package = std::make_unique<WitPackage>();
166+ package->id = package_id_val;
167+ package->source_path = path;
168+
169+ // Add all interfaces
170+ for (const auto &interface : parse_result.interfaces )
171+ {
172+ package->add_interface (interface);
173+ }
174+
175+ // Add to registry
176+ add_package (std::move (package));
150177 }
151-
152- // Add to registry
153- add_package (std::move (package));
154178
155179 return true ;
156180 }
@@ -161,6 +185,81 @@ bool PackageRegistry::load_package(const std::filesystem::path &path)
161185 }
162186}
163187
188+ std::optional<PackageId> PackageRegistry::infer_package_from_path (const std::filesystem::path &path)
189+ {
190+ // Check sibling files in the same directory for package declarations
191+ auto parent_dir = path.parent_path ();
192+
193+ try
194+ {
195+ for (const auto &entry : std::filesystem::directory_iterator (parent_dir))
196+ {
197+ if (entry.is_regular_file () &&
198+ entry.path ().extension () == " .wit" &&
199+ entry.path () != path)
200+ {
201+ try
202+ {
203+ // Read first 1KB of the file to check for package declaration
204+ // This avoids full parsing which could cause issues
205+ std::ifstream file (entry.path ());
206+ if (!file.is_open ())
207+ continue ;
208+
209+ std::string content;
210+ std::string line;
211+ size_t bytes_read = 0 ;
212+ const size_t MAX_BYTES = 1024 ;
213+
214+ while (std::getline (file, line) && bytes_read < MAX_BYTES)
215+ {
216+ bytes_read += line.size () + 1 ;
217+ content += line + " \n " ;
218+
219+ // Look for "package namespace:name@version;" pattern
220+ size_t package_pos = line.find (" package" );
221+ if (package_pos != std::string::npos)
222+ {
223+ // Extract the package name
224+ size_t start = package_pos + 7 ; // After "package"
225+ // Skip whitespace
226+ while (start < line.size () && std::isspace (line[start]))
227+ start++;
228+
229+ // Find the semicolon
230+ size_t end = line.find (' ;' , start);
231+ if (end != std::string::npos)
232+ {
233+ std::string package_spec = line.substr (start, end - start);
234+ // Trim trailing whitespace
235+ while (!package_spec.empty () && std::isspace (package_spec.back ()))
236+ package_spec.pop_back ();
237+
238+ auto package_id = PackageId::parse (package_spec);
239+ if (package_id)
240+ {
241+ return package_id;
242+ }
243+ }
244+ }
245+ }
246+ }
247+ catch (...)
248+ {
249+ // Ignore errors reading sibling files
250+ continue ;
251+ }
252+ }
253+ }
254+ }
255+ catch (...)
256+ {
257+ // Directory iteration error
258+ }
259+
260+ return std::nullopt ;
261+ }
262+
164263WitPackage *PackageRegistry::get_package (const PackageId &id)
165264{
166265 auto key = id.to_string ();
0 commit comments