1use std::collections::HashMap;
4
5use super::ResolvedCrateConfig;
6use crate::core::config::extras::{AdapterConfig, Language};
7use crate::core::config::output::{
8 BuildCommandConfig, CleanConfig, LintConfig, SetupConfig, TestAppRunConfig, TestConfig, UpdateConfig,
9};
10use crate::core::config::tools::LangContext;
11use crate::core::config::{
12 build_defaults, clean_defaults, lint_defaults, setup_defaults, test_apps_run_defaults, test_defaults,
13 update_defaults,
14};
15
16impl ResolvedCrateConfig {
17 pub fn adapter_for_function(&self, fn_name: &str) -> Option<&AdapterConfig> {
24 self.adapters.iter().find(|a| a.name == fn_name)
25 }
26 pub fn package_dir(&self, lang: Language) -> String {
31 if !matches!(
52 lang,
53 Language::Python
54 | Language::Node
55 | Language::Wasm
56 | Language::Elixir
57 | Language::Ruby
58 | Language::Java
59 | Language::Swift
60 | Language::Zig
61 | Language::Dart
62 | Language::Kotlin
63 | Language::KotlinAndroid
64 | Language::R
65 | Language::Go
66 ) && let Some(output_path) = self.output_paths.get(&lang.to_string())
67 {
68 return output_path.to_string_lossy().to_string();
69 }
70
71 let override_path = match lang {
73 Language::Python => self.python.as_ref().and_then(|c| c.scaffold_output.as_ref()),
74 Language::Node => self.node.as_ref().and_then(|c| c.scaffold_output.as_ref()),
75 Language::Ruby => self.ruby.as_ref().and_then(|c| c.scaffold_output.as_ref()),
76 Language::Php => self.php.as_ref().and_then(|c| c.scaffold_output.as_ref()),
77 Language::Elixir => self.elixir.as_ref().and_then(|c| c.scaffold_output.as_ref()),
78 _ => None,
79 };
80 if let Some(p) = override_path {
81 return p.to_string_lossy().to_string();
82 }
83
84 match lang {
86 Language::Python => "packages/python".to_string(),
87 Language::Node => self
98 .node
99 .as_ref()
100 .and_then(|c| c.crate_dir.as_ref())
101 .map(|s| s.to_string())
102 .unwrap_or_else(|| format!("crates/{}-node", self.name)),
103 Language::Wasm => self
104 .wasm
105 .as_ref()
106 .and_then(|c| c.crate_dir.as_ref())
107 .map(|s| s.to_string())
108 .unwrap_or_else(|| format!("crates/{}-wasm", self.name)),
109 Language::Ruby => "packages/ruby".to_string(),
110 Language::Php => "packages/php".to_string(),
111 Language::Elixir => "packages/elixir".to_string(),
112 Language::Go => match self.go.as_ref().and_then(|c| c.module_major) {
113 Some(n) if n >= 2 => format!("packages/go/v{n}"),
114 _ => "packages/go".to_string(),
115 },
116 Language::KotlinAndroid => "packages/kotlin-android".to_string(),
117 _ => format!("packages/{lang}"),
118 }
119 }
120
121 pub fn run_wrapper_for_language(&self, lang: Language) -> Option<&str> {
123 match lang {
124 Language::Python => self.python.as_ref().and_then(|c| c.run_wrapper.as_deref()),
125 Language::Node => self.node.as_ref().and_then(|c| c.run_wrapper.as_deref()),
126 Language::Ruby => self.ruby.as_ref().and_then(|c| c.run_wrapper.as_deref()),
127 Language::Php => self.php.as_ref().and_then(|c| c.run_wrapper.as_deref()),
128 Language::Elixir => self.elixir.as_ref().and_then(|c| c.run_wrapper.as_deref()),
129 Language::Wasm => self.wasm.as_ref().and_then(|c| c.run_wrapper.as_deref()),
130 Language::Go => self.go.as_ref().and_then(|c| c.run_wrapper.as_deref()),
131 Language::Java => self.java.as_ref().and_then(|c| c.run_wrapper.as_deref()),
132 Language::Csharp => self.csharp.as_ref().and_then(|c| c.run_wrapper.as_deref()),
133 Language::R => self.r.as_ref().and_then(|c| c.run_wrapper.as_deref()),
134 Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.run_wrapper.as_deref()),
135 Language::KotlinAndroid => self.kotlin_android.as_ref().and_then(|c| c.run_wrapper.as_deref()),
136 Language::Dart => self.dart.as_ref().and_then(|c| c.run_wrapper.as_deref()),
137 Language::Swift => self.swift.as_ref().and_then(|c| c.run_wrapper.as_deref()),
138 Language::Gleam => self.gleam.as_ref().and_then(|c| c.run_wrapper.as_deref()),
139 Language::Zig => self.zig.as_ref().and_then(|c| c.run_wrapper.as_deref()),
140 Language::Ffi | Language::Rust | Language::C | Language::Jni => None,
141 }
142 }
143
144 pub fn extra_lint_paths_for_language(&self, lang: Language) -> &[String] {
146 match lang {
147 Language::Python => self
148 .python
149 .as_ref()
150 .map(|c| c.extra_lint_paths.as_slice())
151 .unwrap_or(&[]),
152 Language::Node => self.node.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
153 Language::Ruby => self.ruby.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
154 Language::Php => self.php.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
155 Language::Elixir => self
156 .elixir
157 .as_ref()
158 .map(|c| c.extra_lint_paths.as_slice())
159 .unwrap_or(&[]),
160 Language::Wasm => self.wasm.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
161 Language::Go => self.go.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
162 Language::Java => self.java.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
163 Language::Csharp => self
164 .csharp
165 .as_ref()
166 .map(|c| c.extra_lint_paths.as_slice())
167 .unwrap_or(&[]),
168 Language::R => self.r.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
169 Language::Kotlin => self
170 .kotlin
171 .as_ref()
172 .map(|c| c.extra_lint_paths.as_slice())
173 .unwrap_or(&[]),
174 Language::KotlinAndroid => self
175 .kotlin_android
176 .as_ref()
177 .map(|c| c.extra_lint_paths.as_slice())
178 .unwrap_or(&[]),
179 Language::Dart => self.dart.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
180 Language::Swift => self
181 .swift
182 .as_ref()
183 .map(|c| c.extra_lint_paths.as_slice())
184 .unwrap_or(&[]),
185 Language::Gleam => self
186 .gleam
187 .as_ref()
188 .map(|c| c.extra_lint_paths.as_slice())
189 .unwrap_or(&[]),
190 Language::Zig => self.zig.as_ref().map(|c| c.extra_lint_paths.as_slice()).unwrap_or(&[]),
191 Language::Ffi | Language::Rust | Language::C | Language::Jni => &[],
192 }
193 }
194
195 pub fn project_file_for_language(&self, lang: Language) -> Option<&str> {
197 match lang {
198 Language::Java => self.java.as_ref().and_then(|c| c.project_file.as_deref()),
199 Language::Csharp => self.csharp.as_ref().and_then(|c| c.project_file.as_deref()),
200 _ => None,
201 }
202 }
203
204 pub fn lint_config_for_language(&self, lang: Language) -> LintConfig {
206 let lang_str = lang.to_string();
207 if let Some(explicit) = self.lint.get(&lang_str) {
208 return explicit.clone();
209 }
210 let output_dir = self.package_dir(lang);
211 let run_wrapper = self.run_wrapper_for_language(lang);
212 let extra_lint_paths = self.extra_lint_paths_for_language(lang);
213 let project_file = self.project_file_for_language(lang);
214 let ctx = LangContext {
215 tools: &self.tools,
216 run_wrapper,
217 extra_lint_paths,
218 project_file,
219 };
220 lint_defaults::default_lint_config(lang, &output_dir, &ctx)
221 }
222
223 pub fn update_config_for_language(&self, lang: Language) -> UpdateConfig {
225 let lang_str = lang.to_string();
226 if let Some(explicit) = self.update.get(&lang_str) {
227 return explicit.clone();
228 }
229 let output_dir = self.package_dir(lang);
230 let ctx = LangContext {
231 tools: &self.tools,
232 run_wrapper: None,
233 extra_lint_paths: &[],
234 project_file: None,
235 };
236 update_defaults::default_update_config(lang, &output_dir, &ctx)
237 }
238
239 pub fn test_apps_run_config_for_name(&self, name: &str) -> TestAppRunConfig {
247 let test_apps_dir = self
248 .e2e
249 .as_ref()
250 .map(|e2e| e2e.registry.output.as_str())
251 .unwrap_or("test_apps");
252 if let Some(explicit) = self.e2e.as_ref().and_then(|e2e| e2e.registry.run.get(name)) {
253 return explicit.clone();
254 }
255 let ctx = LangContext {
256 tools: &self.tools,
257 run_wrapper: None,
258 extra_lint_paths: &[],
259 project_file: None,
260 };
261 let parsed: Result<Language, _> = toml::Value::String(name.to_string()).try_into();
264 match parsed {
265 Ok(lang) => {
266 let published_version = self
272 .e2e
273 .as_ref()
274 .and_then(|e2e| e2e.resolve_package(name))
275 .and_then(|pkg| pkg.version)
276 .or_else(|| self.resolved_version());
277 let go_module = if lang == Language::Go {
279 Some(self.go_module())
280 } else {
281 None
282 };
283 test_apps_run_defaults::default_test_apps_run_config(
284 lang,
285 test_apps_dir,
286 &ctx,
287 published_version.as_deref(),
288 go_module.as_deref(),
289 )
290 }
291 Err(_) => test_apps_run_defaults::default_test_apps_run_config_for_name(name, test_apps_dir, &ctx),
292 }
293 }
294
295 pub fn test_config_for_language(&self, lang: Language) -> TestConfig {
297 let lang_str = lang.to_string();
298 if let Some(explicit) = self.test.get(&lang_str) {
299 return explicit.clone();
300 }
301 let output_dir = self.package_dir(lang);
302 let run_wrapper = self.run_wrapper_for_language(lang);
303 let project_file = self.project_file_for_language(lang);
304 let ctx = LangContext {
305 tools: &self.tools,
306 run_wrapper,
307 extra_lint_paths: &[],
308 project_file,
309 };
310 test_defaults::default_test_config(lang, &output_dir, &ctx)
311 }
312
313 pub fn setup_config_for_language(&self, lang: Language) -> SetupConfig {
315 let lang_str = lang.to_string();
316 if let Some(explicit) = self.setup.get(&lang_str) {
317 return explicit.clone();
318 }
319 let output_dir = self.package_dir(lang);
320 let ctx = LangContext {
321 tools: &self.tools,
322 run_wrapper: None,
323 extra_lint_paths: &[],
324 project_file: None,
325 };
326 setup_defaults::default_setup_config(lang, &output_dir, &ctx)
327 }
328
329 pub fn clean_config_for_language(&self, lang: Language) -> CleanConfig {
331 let lang_str = lang.to_string();
332 if let Some(explicit) = self.clean.get(&lang_str) {
333 return explicit.clone();
334 }
335 let output_dir = self.package_dir(lang);
336 let ctx = LangContext {
337 tools: &self.tools,
338 run_wrapper: None,
339 extra_lint_paths: &[],
340 project_file: None,
341 };
342 clean_defaults::default_clean_config(lang, &output_dir, &ctx)
343 }
344
345 pub fn build_command_config_for_language(&self, lang: Language) -> BuildCommandConfig {
347 let lang_str = lang.to_string();
348 let output_dir = self.package_dir(lang);
349 let run_wrapper = self.run_wrapper_for_language(lang);
350 let project_file = self.project_file_for_language(lang);
351 let ctx = LangContext {
352 tools: &self.tools,
353 run_wrapper,
354 extra_lint_paths: &[],
355 project_file,
356 };
357 let default = build_defaults::default_build_config(lang, &output_dir, &self.name, &ctx);
358 if let Some(explicit) = self.build_commands.get(&lang_str) {
359 default.merge_overlay(explicit)
360 } else {
361 default
362 }
363 }
364
365 pub fn features_for_language(&self, lang: Language) -> &[String] {
367 let override_features = match lang {
368 Language::Python => self.python.as_ref().and_then(|c| c.features.as_deref()),
369 Language::Node => self.node.as_ref().and_then(|c| c.features.as_deref()),
370 Language::Ruby => self.ruby.as_ref().and_then(|c| c.features.as_deref()),
371 Language::Php => self.php.as_ref().and_then(|c| c.features.as_deref()),
372 Language::Elixir => self.elixir.as_ref().and_then(|c| c.features.as_deref()),
373 Language::Wasm => self.wasm.as_ref().and_then(|c| c.features.as_deref()),
374 Language::Ffi => self.ffi.as_ref().and_then(|c| c.features.as_deref()),
375 Language::Go => self.go.as_ref().and_then(|c| c.features.as_deref()),
376 Language::Java => self.java.as_ref().and_then(|c| c.features.as_deref()),
377 Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.features.as_deref()),
378 Language::KotlinAndroid => self.kotlin_android.as_ref().and_then(|c| c.features.as_deref()),
379 Language::Csharp => self.csharp.as_ref().and_then(|c| c.features.as_deref()),
380 Language::R => self.r.as_ref().and_then(|c| c.features.as_deref()),
381 Language::Zig => self.zig.as_ref().and_then(|c| c.features.as_deref()),
382 Language::Dart => self.dart.as_ref().and_then(|c| c.features.as_deref()),
383 Language::Swift => self.swift.as_ref().and_then(|c| c.features.as_deref()),
384 Language::Gleam => self.gleam.as_ref().and_then(|c| c.features.as_deref()),
385 Language::Rust | Language::C | Language::Jni => None,
386 };
387 override_features.unwrap_or(&self.features)
388 }
389
390 pub fn extra_deps_for_language(&self, lang: Language) -> HashMap<String, toml::Value> {
392 let mut deps = self.extra_dependencies.clone();
393 let lang_deps = match lang {
394 Language::Python => self.python.as_ref().map(|c| &c.extra_dependencies),
395 Language::Node => self.node.as_ref().map(|c| &c.extra_dependencies),
396 Language::Ruby => self.ruby.as_ref().map(|c| &c.extra_dependencies),
397 Language::Php => self.php.as_ref().map(|c| &c.extra_dependencies),
398 Language::Elixir => self.elixir.as_ref().map(|c| &c.extra_dependencies),
399 Language::Wasm => self.wasm.as_ref().map(|c| &c.extra_dependencies),
400 Language::Dart => self.dart.as_ref().map(|c| &c.extra_dependencies),
401 Language::Swift => self.swift.as_ref().map(|c| &c.extra_dependencies),
402 _ => None,
403 };
404 if let Some(lang_deps) = lang_deps {
405 deps.extend(lang_deps.iter().map(|(k, v)| (k.clone(), v.clone())));
406 }
407 let exclude: &[String] = match lang {
408 Language::Wasm => self
409 .wasm
410 .as_ref()
411 .map(|c| c.exclude_extra_dependencies.as_slice())
412 .unwrap_or(&[]),
413 Language::Dart => self
414 .dart
415 .as_ref()
416 .map(|c| c.exclude_extra_dependencies.as_slice())
417 .unwrap_or(&[]),
418 Language::Swift => self
419 .swift
420 .as_ref()
421 .map(|c| c.exclude_extra_dependencies.as_slice())
422 .unwrap_or(&[]),
423 _ => &[],
424 };
425 for key in exclude {
426 deps.remove(key);
427 }
428 deps
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use crate::core::config::extras::Language;
435 use crate::core::config::new_config::NewAlefConfig;
436
437 fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
438 let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
439 cfg.resolve().unwrap().remove(0)
440 }
441
442 fn minimal() -> super::super::ResolvedCrateConfig {
443 resolved_one(
444 r#"
445[workspace]
446languages = ["python", "node"]
447
448[[crates]]
449name = "test-lib"
450sources = ["src/lib.rs"]
451"#,
452 )
453 }
454
455 #[test]
456 fn resolved_lint_config_inherits_workspace_when_crate_unset() {
457 let r = resolved_one(
458 r#"
459[workspace]
460languages = ["python"]
461
462[workspace.lint.python]
463check = "ruff check ."
464
465[[crates]]
466name = "test-lib"
467sources = ["src/lib.rs"]
468"#,
469 );
470 let lint = r.lint_config_for_language(Language::Python);
471 assert_eq!(lint.check.unwrap().commands(), vec!["ruff check ."]);
472 }
473
474 #[test]
475 fn resolved_lint_config_crate_overrides_workspace_field_wholesale() {
476 let r = resolved_one(
477 r#"
478[workspace]
479languages = ["python"]
480
481[workspace.lint.python]
482check = "ruff check ."
483
484[[crates]]
485name = "test-lib"
486sources = ["src/lib.rs"]
487
488[crates.lint.python]
489check = "ruff check crates/test-lib-py/"
490"#,
491 );
492 let lint = r.lint_config_for_language(Language::Python);
493 assert_eq!(lint.check.unwrap().commands(), vec!["ruff check crates/test-lib-py/"]);
494 }
495
496 #[test]
497 fn resolved_features_per_language_overrides_crate_default() {
498 let r = resolved_one(
499 r#"
500[workspace]
501languages = ["python"]
502
503[[crates]]
504name = "test-lib"
505sources = ["src/lib.rs"]
506features = ["base"]
507
508[crates.python]
509features = ["python-extra"]
510"#,
511 );
512 assert_eq!(r.features_for_language(Language::Python), &["python-extra"]);
513 assert_eq!(r.features_for_language(Language::Node), &["base"]);
514 }
515
516 #[test]
517 fn resolved_extra_deps_crate_value_wins_on_key_collision() {
518 let r = resolved_one(
519 r#"
520[workspace]
521languages = ["python"]
522
523[[crates]]
524name = "test-lib"
525sources = ["src/lib.rs"]
526
527[crates.extra_dependencies]
528tokio = "1"
529
530[crates.python]
531extra_dependencies = { tokio = "2" }
532"#,
533 );
534 let deps = r.extra_deps_for_language(Language::Python);
535 let tokio = deps.get("tokio").unwrap().as_str().unwrap();
536 assert_eq!(tokio, "2", "per-language extra_dep should win on collision");
537 }
538
539 #[test]
540 fn resolved_extra_deps_excludes_apply_after_merge() {
541 let r = resolved_one(
542 r#"
543[workspace]
544languages = ["wasm"]
545
546[[crates]]
547name = "test-lib"
548sources = ["src/lib.rs"]
549
550[crates.extra_dependencies]
551tokio = "1"
552serde = "1"
553
554[crates.wasm]
555exclude_extra_dependencies = ["tokio"]
556"#,
557 );
558 let deps = r.extra_deps_for_language(Language::Wasm);
559 assert!(!deps.contains_key("tokio"), "excluded dep should be absent");
560 assert!(deps.contains_key("serde"), "non-excluded dep should be present");
561 }
562
563 #[test]
564 fn resolved_extra_deps_includes_swift_overrides() {
565 let r = resolved_one(
566 r#"
567[workspace]
568languages = ["swift"]
569
570[[crates]]
571name = "test-lib"
572sources = ["src/lib.rs"]
573
574[crates.extra_dependencies]
575serde = "1"
576
577[crates.swift.extra_dependencies]
578tokio = "1"
579"#,
580 );
581 let deps = r.extra_deps_for_language(Language::Swift);
582 assert!(deps.contains_key("serde"), "crate-level dep should be present");
583 assert!(deps.contains_key("tokio"), "Swift dep should be present");
584 }
585
586 #[test]
587 fn package_dir_defaults_are_correct() {
588 let r = minimal();
589 assert_eq!(r.package_dir(Language::Python), "packages/python");
590 assert_eq!(r.package_dir(Language::Node), format!("crates/{}-node", r.name));
593 assert_eq!(r.package_dir(Language::Wasm), format!("crates/{}-wasm", r.name));
594 assert_eq!(r.package_dir(Language::Ruby), "packages/ruby");
595 assert_eq!(r.package_dir(Language::Go), "packages/go");
597 assert_eq!(r.package_dir(Language::Java), "packages/java");
598 assert_eq!(r.package_dir(Language::Kotlin), "packages/kotlin");
599 assert_eq!(r.package_dir(Language::KotlinAndroid), "packages/kotlin-android");
600 }
601
602 #[test]
603 fn package_dir_go_with_module_major() {
604 let r_none = resolved_one(
608 r#"
609[workspace]
610languages = ["go"]
611
612[[crates]]
613name = "test-lib"
614sources = ["src/lib.rs"]
615"#,
616 );
617 assert_eq!(
618 r_none.package_dir(Language::Go),
619 "packages/go",
620 "Go without module_major should default to 'packages/go'"
621 );
622
623 let r_v1 = resolved_one(
624 r#"
625[workspace]
626languages = ["go"]
627
628[[crates]]
629name = "test-lib"
630sources = ["src/lib.rs"]
631
632[crates.go]
633module_major = 1
634"#,
635 );
636 assert_eq!(
637 r_v1.package_dir(Language::Go),
638 "packages/go",
639 "Go with module_major = 1 should use 'packages/go'"
640 );
641
642 let r_v2 = resolved_one(
643 r#"
644[workspace]
645languages = ["go"]
646
647[[crates]]
648name = "test-lib"
649sources = ["src/lib.rs"]
650
651[crates.go]
652module_major = 2
653"#,
654 );
655 assert_eq!(
656 r_v2.package_dir(Language::Go),
657 "packages/go/v2",
658 "Go with module_major = 2 should use 'packages/go/v2'"
659 );
660
661 let r_v5 = resolved_one(
662 r#"
663[workspace]
664languages = ["go"]
665
666[[crates]]
667name = "test-lib"
668sources = ["src/lib.rs"]
669
670[crates.go]
671module_major = 5
672"#,
673 );
674 assert_eq!(
675 r_v5.package_dir(Language::Go),
676 "packages/go/v5",
677 "Go with module_major = 5 should use 'packages/go/v5'"
678 );
679 }
680
681 #[test]
682 fn package_dir_r_ignores_source_output_override() {
683 let r = resolved_one(
689 r#"
690[workspace]
691languages = ["r"]
692
693[[crates]]
694name = "demo"
695sources = ["src/lib.rs"]
696
697[crates.output]
698r = "packages/r/src/rust/src/"
699"#,
700 );
701 assert_eq!(r.package_dir(Language::R), "packages/r");
702 }
703
704 #[test]
705 fn package_dir_python_ignores_source_output_override() {
706 let r = resolved_one(
710 r#"
711[workspace]
712languages = ["python"]
713
714[[crates]]
715name = "demo"
716sources = ["src/lib.rs"]
717
718[crates.output]
719python = "crates/demo-py/src/"
720"#,
721 );
722 assert_eq!(r.package_dir(Language::Python), "packages/python");
723 }
724
725 #[test]
726 fn package_dir_kotlin_ignores_source_output_override() {
727 let r = resolved_one(
732 r#"
733[workspace]
734languages = ["kotlin"]
735
736[[crates]]
737name = "demo"
738sources = ["src/lib.rs"]
739
740[crates.kotlin]
741package = "dev.demo"
742target = "jvm"
743
744[crates.output]
745kotlin = "packages/kotlin/src/main/kotlin/dev/demo/kt/"
746"#,
747 );
748 assert_eq!(r.package_dir(Language::Kotlin), "packages/kotlin");
749 }
750
751 #[test]
752 fn package_dir_node_crate_dir_override_takes_precedence() {
753 let r = resolved_one(
754 r#"
755[workspace]
756languages = ["node"]
757
758[[crates]]
759name = "sample-markdown-rs"
760sources = ["src/lib.rs"]
761
762[crates.node]
763crate_dir = "crates/sample-markdown-node"
764"#,
765 );
766 assert_eq!(
767 r.package_dir(Language::Node),
768 "crates/sample-markdown-node",
769 "crate_dir override should be used instead of default formula"
770 );
771 }
772
773 #[test]
774 fn package_dir_wasm_crate_dir_override_takes_precedence() {
775 let r = resolved_one(
776 r#"
777[workspace]
778languages = ["wasm"]
779
780[[crates]]
781name = "sample-markdown-rs"
782sources = ["src/lib.rs"]
783
784[crates.wasm]
785crate_dir = "crates/sample-markdown-wasm"
786"#,
787 );
788 assert_eq!(
789 r.package_dir(Language::Wasm),
790 "crates/sample-markdown-wasm",
791 "crate_dir override should be used instead of default formula"
792 );
793 }
794
795 #[test]
796 fn package_dir_node_without_override_uses_default_formula() {
797 let r = resolved_one(
798 r#"
799[workspace]
800languages = ["node"]
801
802[[crates]]
803name = "test-lib"
804sources = ["src/lib.rs"]
805"#,
806 );
807 assert_eq!(
808 r.package_dir(Language::Node),
809 "crates/test-lib-node",
810 "default formula should apply when crate_dir is not set"
811 );
812 }
813
814 #[test]
815 fn package_dir_wasm_without_override_uses_default_formula() {
816 let r = resolved_one(
817 r#"
818[workspace]
819languages = ["wasm"]
820
821[[crates]]
822name = "test-lib"
823sources = ["src/lib.rs"]
824"#,
825 );
826 assert_eq!(
827 r.package_dir(Language::Wasm),
828 "crates/test-lib-wasm",
829 "default formula should apply when crate_dir is not set"
830 );
831 }
832
833 #[test]
834 fn package_dir_sample_markdown_scenario_with_both_overrides() {
835 let r = resolved_one(
838 r#"
839[workspace]
840languages = ["node", "wasm"]
841
842[[crates]]
843name = "sample-markdown-rs"
844sources = ["src/lib.rs"]
845
846[crates.node]
847crate_dir = "crates/sample-markdown-node"
848
849[crates.wasm]
850crate_dir = "crates/sample-markdown-wasm"
851"#,
852 );
853 assert_eq!(
856 r.package_dir(Language::Node),
857 "crates/sample-markdown-node",
858 "Node override should return exact path without formula"
859 );
860 assert_eq!(
861 r.package_dir(Language::Wasm),
862 "crates/sample-markdown-wasm",
863 "Wasm override should return exact path without formula"
864 );
865 }
866}