/home/noah/src/ruchy/src/wasm/wit.rs
Line | Count | Source |
1 | | //! WIT (WebAssembly Interface Type) generation for Ruchy (RUCHY-0819) |
2 | | //! |
3 | | //! Generates WIT interface definitions from Ruchy code for component interoperability. |
4 | | |
5 | | use anyhow::{Context, Result}; |
6 | | use serde::{Deserialize, Serialize}; |
7 | | use std::collections::{HashMap, HashSet}; |
8 | | use std::path::Path; |
9 | | use std::fs; |
10 | | use std::fmt; |
11 | | |
12 | | /// WIT interface definition |
13 | | #[derive(Debug, Clone)] |
14 | | pub struct WitInterface { |
15 | | /// Interface name |
16 | | pub name: String, |
17 | | |
18 | | /// Interface version |
19 | | pub version: String, |
20 | | |
21 | | /// Package information |
22 | | pub package: PackageInfo, |
23 | | |
24 | | /// Type definitions |
25 | | pub types: Vec<TypeDefinition>, |
26 | | |
27 | | /// Function definitions |
28 | | pub functions: Vec<FunctionDefinition>, |
29 | | |
30 | | /// Resource definitions |
31 | | pub resources: Vec<ResourceDefinition>, |
32 | | |
33 | | /// World definition |
34 | | pub world: Option<WorldDefinition>, |
35 | | } |
36 | | |
37 | | /// WIT interface generator |
38 | | pub struct WitGenerator { |
39 | | /// Generation configuration |
40 | | config: WitConfig, |
41 | | |
42 | | /// Type registry |
43 | | _type_registry: TypeRegistry, |
44 | | |
45 | | /// Import tracking |
46 | | _imports: HashSet<String>, |
47 | | } |
48 | | |
49 | | /// Configuration for WIT generation |
50 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
51 | | pub struct WitConfig { |
52 | | /// Generate documentation comments |
53 | | pub include_docs: bool, |
54 | | |
55 | | /// Generate type aliases |
56 | | pub use_type_aliases: bool, |
57 | | |
58 | | /// Generate resource types |
59 | | pub generate_resources: bool, |
60 | | |
61 | | /// Component model version |
62 | | pub component_model_version: String, |
63 | | |
64 | | /// Custom type mappings |
65 | | pub type_mappings: HashMap<String, String>, |
66 | | |
67 | | /// World name |
68 | | pub world_name: Option<String>, |
69 | | } |
70 | | |
71 | | /// Interface definition structure |
72 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
73 | | pub struct InterfaceDefinition { |
74 | | /// Interface name |
75 | | pub name: String, |
76 | | |
77 | | /// Interface functions |
78 | | pub functions: Vec<InterfaceFunction>, |
79 | | |
80 | | /// Interface types |
81 | | pub types: Vec<InterfaceType>, |
82 | | |
83 | | /// Documentation |
84 | | pub documentation: Option<String>, |
85 | | } |
86 | | |
87 | | /// Package information |
88 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
89 | | pub struct PackageInfo { |
90 | | /// Package namespace |
91 | | pub namespace: String, |
92 | | |
93 | | /// Package name |
94 | | pub name: String, |
95 | | |
96 | | /// Package version |
97 | | pub version: String, |
98 | | } |
99 | | |
100 | | /// Type definition in WIT |
101 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
102 | | pub struct TypeDefinition { |
103 | | /// Type name |
104 | | pub name: String, |
105 | | |
106 | | /// Type kind |
107 | | pub kind: TypeKind, |
108 | | |
109 | | /// Documentation |
110 | | pub documentation: Option<String>, |
111 | | } |
112 | | |
113 | | /// Function definition in WIT |
114 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
115 | | pub struct FunctionDefinition { |
116 | | /// Function name |
117 | | pub name: String, |
118 | | |
119 | | /// Parameters |
120 | | pub params: Vec<Parameter>, |
121 | | |
122 | | /// Return type |
123 | | pub return_type: Option<WitType>, |
124 | | |
125 | | /// Whether function is async |
126 | | pub is_async: bool, |
127 | | |
128 | | /// Documentation |
129 | | pub documentation: Option<String>, |
130 | | } |
131 | | |
132 | | /// Resource definition in WIT |
133 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
134 | | pub struct ResourceDefinition { |
135 | | /// Resource name |
136 | | pub name: String, |
137 | | |
138 | | /// Resource methods |
139 | | pub methods: Vec<ResourceMethod>, |
140 | | |
141 | | /// Constructor |
142 | | pub constructor: Option<FunctionDefinition>, |
143 | | |
144 | | /// Documentation |
145 | | pub documentation: Option<String>, |
146 | | } |
147 | | |
148 | | /// World definition for component composition |
149 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
150 | | pub struct WorldDefinition { |
151 | | /// World name |
152 | | pub name: String, |
153 | | |
154 | | /// Imports |
155 | | pub imports: Vec<WorldImport>, |
156 | | |
157 | | /// Exports |
158 | | pub exports: Vec<WorldExport>, |
159 | | |
160 | | /// Documentation |
161 | | pub documentation: Option<String>, |
162 | | } |
163 | | |
164 | | /// Type kinds in WIT |
165 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
166 | | pub enum TypeKind { |
167 | | /// Record type (struct) |
168 | | Record(Vec<Field>), |
169 | | /// Variant type (enum) |
170 | | Variant(Vec<VariantCase>), |
171 | | /// Flags type (bitflags) |
172 | | Flags(Vec<String>), |
173 | | /// Tuple type |
174 | | Tuple(Vec<WitType>), |
175 | | /// List type |
176 | | List(Box<WitType>), |
177 | | /// Option type |
178 | | Option(Box<WitType>), |
179 | | /// Result type |
180 | | Result { |
181 | | ok: Option<Box<WitType>>, |
182 | | err: Option<Box<WitType>>, |
183 | | }, |
184 | | /// Type alias |
185 | | Alias(Box<WitType>), |
186 | | } |
187 | | |
188 | | /// WIT type representation |
189 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
190 | | pub enum WitType { |
191 | | /// Boolean |
192 | | Bool, |
193 | | /// Unsigned 8-bit integer |
194 | | U8, |
195 | | /// Unsigned 16-bit integer |
196 | | U16, |
197 | | /// Unsigned 32-bit integer |
198 | | U32, |
199 | | /// Unsigned 64-bit integer |
200 | | U64, |
201 | | /// Signed 8-bit integer |
202 | | S8, |
203 | | /// Signed 16-bit integer |
204 | | S16, |
205 | | /// Signed 32-bit integer |
206 | | S32, |
207 | | /// Signed 64-bit integer |
208 | | S64, |
209 | | /// 32-bit float |
210 | | F32, |
211 | | /// 64-bit float |
212 | | F64, |
213 | | /// Character |
214 | | Char, |
215 | | /// String |
216 | | String, |
217 | | /// Named type reference |
218 | | Named(String), |
219 | | /// List type |
220 | | List(Box<WitType>), |
221 | | /// Option type |
222 | | Option(Box<WitType>), |
223 | | /// Result type |
224 | | Result { |
225 | | ok: Option<Box<WitType>>, |
226 | | err: Option<Box<WitType>>, |
227 | | }, |
228 | | /// Tuple type |
229 | | Tuple(Vec<WitType>), |
230 | | } |
231 | | |
232 | | /// Record field |
233 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
234 | | pub struct Field { |
235 | | /// Field name |
236 | | pub name: String, |
237 | | |
238 | | /// Field type |
239 | | pub field_type: WitType, |
240 | | |
241 | | /// Documentation |
242 | | pub documentation: Option<String>, |
243 | | } |
244 | | |
245 | | /// Variant case |
246 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
247 | | pub struct VariantCase { |
248 | | /// Case name |
249 | | pub name: String, |
250 | | |
251 | | /// Associated data |
252 | | pub payload: Option<WitType>, |
253 | | |
254 | | /// Documentation |
255 | | pub documentation: Option<String>, |
256 | | } |
257 | | |
258 | | /// Function parameter |
259 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
260 | | pub struct Parameter { |
261 | | /// Parameter name |
262 | | pub name: String, |
263 | | |
264 | | /// Parameter type |
265 | | pub param_type: WitType, |
266 | | } |
267 | | |
268 | | /// Resource method |
269 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
270 | | pub struct ResourceMethod { |
271 | | /// Method name |
272 | | pub name: String, |
273 | | |
274 | | /// Method kind |
275 | | pub kind: MethodKind, |
276 | | |
277 | | /// Method definition |
278 | | pub function: FunctionDefinition, |
279 | | } |
280 | | |
281 | | /// Method kinds for resources |
282 | | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
283 | | pub enum MethodKind { |
284 | | /// Constructor method |
285 | | Constructor, |
286 | | /// Static method |
287 | | Static, |
288 | | /// Instance method |
289 | | Instance, |
290 | | } |
291 | | |
292 | | /// Interface function |
293 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
294 | | pub struct InterfaceFunction { |
295 | | /// Function name |
296 | | pub name: String, |
297 | | |
298 | | /// Parameters |
299 | | pub params: Vec<(String, String)>, |
300 | | |
301 | | /// Return type |
302 | | pub return_type: Option<String>, |
303 | | } |
304 | | |
305 | | /// Interface type |
306 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
307 | | pub struct InterfaceType { |
308 | | /// Type name |
309 | | pub name: String, |
310 | | |
311 | | /// Type definition |
312 | | pub definition: String, |
313 | | } |
314 | | |
315 | | /// World import |
316 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
317 | | pub struct WorldImport { |
318 | | /// Import name |
319 | | pub name: String, |
320 | | |
321 | | /// Import interface |
322 | | pub interface: String, |
323 | | } |
324 | | |
325 | | /// World export |
326 | | #[derive(Debug, Clone, Serialize, Deserialize)] |
327 | | pub struct WorldExport { |
328 | | /// Export name |
329 | | pub name: String, |
330 | | |
331 | | /// Export interface |
332 | | pub interface: String, |
333 | | } |
334 | | |
335 | | /// Type registry for managing type definitions |
336 | | struct TypeRegistry { |
337 | | /// Registered types |
338 | | _types: HashMap<String, TypeDefinition>, |
339 | | |
340 | | /// Type dependencies |
341 | | _dependencies: HashMap<String, HashSet<String>>, |
342 | | } |
343 | | |
344 | | impl Default for WitConfig { |
345 | 0 | fn default() -> Self { |
346 | 0 | Self { |
347 | 0 | include_docs: true, |
348 | 0 | use_type_aliases: true, |
349 | 0 | generate_resources: true, |
350 | 0 | component_model_version: "0.2.0".to_string(), |
351 | 0 | type_mappings: HashMap::new(), |
352 | 0 | world_name: None, |
353 | 0 | } |
354 | 0 | } |
355 | | } |
356 | | |
357 | | impl Default for WitGenerator { |
358 | 0 | fn default() -> Self { |
359 | 0 | Self::new() |
360 | 0 | } |
361 | | } |
362 | | |
363 | | impl WitGenerator { |
364 | | /// Create a new WIT generator with default config |
365 | 0 | pub fn new() -> Self { |
366 | 0 | Self { |
367 | 0 | config: WitConfig::default(), |
368 | 0 | _type_registry: TypeRegistry::new(), |
369 | 0 | _imports: HashSet::new(), |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | | /// Create a new WIT generator with specific config |
374 | 0 | pub fn new_with_config(config: WitConfig) -> Self { |
375 | 0 | Self { |
376 | 0 | config, |
377 | 0 | _type_registry: TypeRegistry::new(), |
378 | 0 | _imports: HashSet::new(), |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | /// Set the world name |
383 | 0 | pub fn with_world(&mut self, world: &str) -> &mut Self { |
384 | 0 | self.config.world_name = Some(world.to_string()); |
385 | 0 | self |
386 | 0 | } |
387 | | |
388 | | /// Generate WIT interface from component |
389 | 0 | pub fn generate(&mut self, component: &super::component::WasmComponent) -> Result<WitInterface> { |
390 | 0 | self.generate_from_component(component) |
391 | 0 | } |
392 | | |
393 | | /// Generate WIT interface from component |
394 | 0 | pub fn generate_from_component(&mut self, _component: &super::component::WasmComponent) -> Result<WitInterface> { |
395 | | // In a real implementation, analyze the component's exports and imports |
396 | 0 | self.generate_default() |
397 | 0 | } |
398 | | |
399 | | /// Generate WIT interface from Ruchy source |
400 | 0 | pub fn generate_from_source(&mut self, _source: &str) -> Result<WitInterface> { |
401 | | // In a real implementation, this would: |
402 | | // 1. Parse Ruchy source code |
403 | | // 2. Extract type definitions |
404 | | // 3. Extract function signatures |
405 | | // 4. Generate corresponding WIT definitions |
406 | | |
407 | | // For now, create a sample interface |
408 | 0 | let interface = WitInterface { |
409 | 0 | name: "ruchy-component".to_string(), |
410 | 0 | version: "0.1.0".to_string(), |
411 | 0 | package: PackageInfo { |
412 | 0 | namespace: "ruchy".to_string(), |
413 | 0 | name: "component".to_string(), |
414 | 0 | version: "0.1.0".to_string(), |
415 | 0 | }, |
416 | 0 | types: vec![ |
417 | 0 | TypeDefinition { |
418 | 0 | name: "request".to_string(), |
419 | 0 | kind: TypeKind::Record(vec![ |
420 | 0 | Field { |
421 | 0 | name: "method".to_string(), |
422 | 0 | field_type: WitType::String, |
423 | 0 | documentation: Some("HTTP method".to_string()), |
424 | 0 | }, |
425 | 0 | Field { |
426 | 0 | name: "path".to_string(), |
427 | 0 | field_type: WitType::String, |
428 | 0 | documentation: Some("Request path".to_string()), |
429 | 0 | }, |
430 | 0 | ]), |
431 | 0 | documentation: Some("HTTP request type".to_string()), |
432 | 0 | }, |
433 | 0 | ], |
434 | 0 | functions: vec![ |
435 | 0 | FunctionDefinition { |
436 | 0 | name: "handle-request".to_string(), |
437 | 0 | params: vec![ |
438 | 0 | Parameter { |
439 | 0 | name: "req".to_string(), |
440 | 0 | param_type: WitType::Named("request".to_string()), |
441 | 0 | }, |
442 | 0 | ], |
443 | 0 | return_type: Some(WitType::String), |
444 | 0 | is_async: false, |
445 | 0 | documentation: Some("Handle an HTTP request".to_string()), |
446 | 0 | }, |
447 | 0 | ], |
448 | 0 | resources: vec![], |
449 | 0 | world: Some(WorldDefinition { |
450 | 0 | name: "http-handler".to_string(), |
451 | 0 | imports: vec![], |
452 | 0 | exports: vec![ |
453 | 0 | WorldExport { |
454 | 0 | name: "handler".to_string(), |
455 | 0 | interface: "ruchy:component/handler".to_string(), |
456 | 0 | }, |
457 | 0 | ], |
458 | 0 | documentation: Some("HTTP handler world".to_string()), |
459 | 0 | }), |
460 | 0 | }; |
461 | | |
462 | 0 | Ok(interface) |
463 | 0 | } |
464 | | |
465 | | /// Generate a default WIT interface |
466 | 0 | fn generate_default(&mut self) -> Result<WitInterface> { |
467 | 0 | self.generate_from_source("") |
468 | 0 | } |
469 | | |
470 | | /// Add a custom type mapping |
471 | 0 | pub fn add_type_mapping(&mut self, ruchy_type: String, wit_type: String) { |
472 | 0 | self.config.type_mappings.insert(ruchy_type, wit_type); |
473 | 0 | } |
474 | | |
475 | | /// Generate WIT file content |
476 | 0 | pub fn generate_wit_file(&self, interface: &WitInterface) -> String { |
477 | 0 | let mut wit = String::new(); |
478 | | |
479 | | // Package declaration |
480 | 0 | wit.push_str(&format!( |
481 | 0 | "package {}:{}/{}@{};\n\n", |
482 | 0 | interface.package.namespace, |
483 | 0 | interface.package.name, |
484 | 0 | interface.name, |
485 | 0 | interface.version |
486 | 0 | )); |
487 | | |
488 | | // Interface declaration |
489 | 0 | wit.push_str(&format!("interface {} {{\n", interface.name)); |
490 | | |
491 | | // Type definitions |
492 | 0 | for type_def in &interface.types { |
493 | 0 | if let Some(doc) = &type_def.documentation { |
494 | 0 | wit.push_str(&format!(" /// {doc}\n")); |
495 | 0 | } |
496 | 0 | wit.push_str(&format!(" {}\n\n", self.format_type_definition(type_def))); |
497 | | } |
498 | | |
499 | | // Function definitions |
500 | 0 | for func in &interface.functions { |
501 | 0 | if let Some(doc) = &func.documentation { |
502 | 0 | wit.push_str(&format!(" /// {doc}\n")); |
503 | 0 | } |
504 | 0 | wit.push_str(&format!(" {}\n", self.format_function(func))); |
505 | | } |
506 | | |
507 | 0 | wit.push_str("}\n\n"); |
508 | | |
509 | | // World definition |
510 | 0 | if let Some(world) = &interface.world { |
511 | 0 | wit.push_str(&self.format_world(world)); |
512 | 0 | } |
513 | | |
514 | 0 | wit |
515 | 0 | } |
516 | | |
517 | 0 | fn format_type_definition(&self, type_def: &TypeDefinition) -> String { |
518 | 0 | match &type_def.kind { |
519 | 0 | TypeKind::Record(fields) => { |
520 | 0 | let mut s = format!("record {} {{\n", type_def.name); |
521 | 0 | for field in fields { |
522 | 0 | if let Some(doc) = &field.documentation { |
523 | 0 | s.push_str(&format!(" /// {doc}\n")); |
524 | 0 | } |
525 | 0 | s.push_str(&format!(" {}: {},\n", field.name, self.format_wit_type(&field.field_type))); |
526 | | } |
527 | 0 | s.push_str(" }"); |
528 | 0 | s |
529 | | } |
530 | 0 | TypeKind::Variant(cases) => { |
531 | 0 | let mut s = format!("variant {} {{\n", type_def.name); |
532 | 0 | for case in cases { |
533 | 0 | if let Some(doc) = &case.documentation { |
534 | 0 | s.push_str(&format!(" /// {doc}\n")); |
535 | 0 | } |
536 | 0 | if let Some(payload) = &case.payload { |
537 | 0 | s.push_str(&format!(" {}({}),\n", case.name, self.format_wit_type(payload))); |
538 | 0 | } else { |
539 | 0 | s.push_str(&format!(" {},\n", case.name)); |
540 | 0 | } |
541 | | } |
542 | 0 | s.push_str(" }"); |
543 | 0 | s |
544 | | } |
545 | 0 | TypeKind::Flags(flags) => { |
546 | 0 | let mut s = format!("flags {} {{\n", type_def.name); |
547 | 0 | for flag in flags { |
548 | 0 | s.push_str(&format!(" {flag},\n")); |
549 | 0 | } |
550 | 0 | s.push_str(" }"); |
551 | 0 | s |
552 | | } |
553 | 0 | TypeKind::Alias(wit_type) => { |
554 | 0 | format!("type {} = {}", type_def.name, self.format_wit_type(wit_type)) |
555 | | } |
556 | 0 | _ => format!("type {}", type_def.name), |
557 | | } |
558 | 0 | } |
559 | | |
560 | 0 | fn format_function(&self, func: &FunctionDefinition) -> String { |
561 | 0 | let params = func.params.iter() |
562 | 0 | .map(|p| format!("{}: {}", p.name, self.format_wit_type(&p.param_type))) |
563 | 0 | .collect::<Vec<_>>() |
564 | 0 | .join(", "); |
565 | | |
566 | 0 | let return_part = if let Some(ret) = &func.return_type { |
567 | 0 | format!(" -> {}", self.format_wit_type(ret)) |
568 | | } else { |
569 | 0 | String::new() |
570 | | }; |
571 | | |
572 | 0 | format!("{}: func({}){};", func.name, params, return_part) |
573 | 0 | } |
574 | | |
575 | 0 | fn format_wit_type(&self, wit_type: &WitType) -> String { |
576 | 0 | match wit_type { |
577 | 0 | WitType::Bool => "bool".to_string(), |
578 | 0 | WitType::U8 => "u8".to_string(), |
579 | 0 | WitType::U16 => "u16".to_string(), |
580 | 0 | WitType::U32 => "u32".to_string(), |
581 | 0 | WitType::U64 => "u64".to_string(), |
582 | 0 | WitType::S8 => "s8".to_string(), |
583 | 0 | WitType::S16 => "s16".to_string(), |
584 | 0 | WitType::S32 => "s32".to_string(), |
585 | 0 | WitType::S64 => "s64".to_string(), |
586 | 0 | WitType::F32 => "f32".to_string(), |
587 | 0 | WitType::F64 => "f64".to_string(), |
588 | 0 | WitType::Char => "char".to_string(), |
589 | 0 | WitType::String => "string".to_string(), |
590 | 0 | WitType::Named(name) => name.clone(), |
591 | 0 | WitType::List(inner) => format!("list<{}>", self.format_wit_type(inner)), |
592 | 0 | WitType::Option(inner) => format!("option<{}>", self.format_wit_type(inner)), |
593 | 0 | WitType::Result { ok, err } => { |
594 | 0 | let ok_str = ok.as_ref().map_or_else(|| "_".to_string(), |t| self.format_wit_type(t)); |
595 | 0 | let err_str = err.as_ref().map_or_else(|| "_".to_string(), |t| self.format_wit_type(t)); |
596 | 0 | format!("result<{ok_str}, {err_str}>") |
597 | | } |
598 | 0 | WitType::Tuple(types) => { |
599 | 0 | let types_str = types.iter() |
600 | 0 | .map(|t| self.format_wit_type(t)) |
601 | 0 | .collect::<Vec<_>>() |
602 | 0 | .join(", "); |
603 | 0 | format!("tuple<{types_str}>") |
604 | | } |
605 | | } |
606 | 0 | } |
607 | | |
608 | 0 | fn format_world(&self, world: &WorldDefinition) -> String { |
609 | 0 | let mut s = String::new(); |
610 | | |
611 | 0 | if let Some(doc) = &world.documentation { |
612 | 0 | s.push_str(&format!("/// {doc}\n")); |
613 | 0 | } |
614 | | |
615 | 0 | s.push_str(&format!("world {} {{\n", world.name)); |
616 | | |
617 | | // Imports |
618 | 0 | for import in &world.imports { |
619 | 0 | s.push_str(&format!(" import {}: {};\n", import.name, import.interface)); |
620 | 0 | } |
621 | | |
622 | | // Exports |
623 | 0 | for export in &world.exports { |
624 | 0 | s.push_str(&format!(" export {}: {};\n", export.name, export.interface)); |
625 | 0 | } |
626 | | |
627 | 0 | s.push_str("}\n"); |
628 | 0 | s |
629 | 0 | } |
630 | | } |
631 | | |
632 | | impl fmt::Display for WitInterface { |
633 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
634 | 0 | let generator = WitGenerator::new(); |
635 | 0 | write!(f, "{}", generator.generate_wit_file(self)) |
636 | 0 | } |
637 | | } |
638 | | |
639 | | impl WitInterface { |
640 | | /// Save the WIT interface to a file |
641 | 0 | pub fn save(&self, path: impl AsRef<Path>) -> Result<()> { |
642 | 0 | let generator = WitGenerator::new(); |
643 | 0 | let wit_content = generator.generate_wit_file(self); |
644 | | |
645 | 0 | fs::write(path.as_ref(), wit_content) |
646 | 0 | .with_context(|| format!("Failed to write WIT file to {}", path.as_ref().display()))?; |
647 | | |
648 | 0 | Ok(()) |
649 | 0 | } |
650 | | } |
651 | | |
652 | | impl TypeRegistry { |
653 | 0 | fn new() -> Self { |
654 | 0 | Self { |
655 | 0 | _types: HashMap::new(), |
656 | 0 | _dependencies: HashMap::new(), |
657 | 0 | } |
658 | 0 | } |
659 | | } |