1use std::{
2 fmt::Display,
3 hash::{Hash, Hasher},
4 ops::Deref,
5};
6
7use anyhow::Result;
8use bincode::{Decode, Encode};
9use indexmap::Equivalent;
10use num_bigint::BigInt;
11use rustc_hash::FxHashSet;
12use smallvec::{SmallVec, smallvec};
13use turbo_rcstr::RcStr;
14use turbo_tasks::{FxIndexMap, NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
15use turbo_tasks_fs::FileSystemPath;
16
17use crate::{environment::Environment, issue::IssueSeverity};
18
19#[macro_export]
20macro_rules! definable_name_map_pattern_internal {
21 ($name:ident) => {
22 [stringify!($name).into()]
23 };
24 ($name:ident typeof) => {
25 [stringify!($name).into(), $crate::compile_time_info::DefinableNameSegment::TypeOf]
26 };
27 ($name:ident . $($more:ident).+ typeof) => {
29 $crate::definable_name_map_pattern_internal!($($more).+ typeof, [stringify!($name).into()])
30 };
31 ($name:ident . $($more:ident).+) => {
32 $crate::definable_name_map_pattern_internal!($($more).+, [stringify!($name).into()])
33 };
34 ($name:ident, [$($array:expr),+]) => {
36 [$($array),+, stringify!($name).into()]
37 };
38 ($name:ident . $($more:ident).+, [$($array:expr),+]) => {
39 $crate::definable_name_map_pattern_internal!($($more).+, [$($array),+, stringify!($name).into()])
40 };
41 ($name:ident typeof, [$($array:expr),+]) => {
42 [$($array),+, stringify!($name).into(), $crate::compile_time_info::DefinableNameSegment::TypeOf]
43 };
44 ($name:ident . $($more:ident).+ typeof, [$($array:expr),+]) => {
45 $crate::definable_name_map_pattern_internal!($($more).+ typeof, [$($array),+, stringify!($name).into()])
46 };
47}
48
49#[macro_export]
51macro_rules! definable_name_map_internal {
52 ($map:ident, .. $value:expr) => {
54 for (key, value) in $value {
55 $map.insert(
56 key.into(),
57 value.into()
58 );
59 }
60 };
61 ($map:ident, .. $value:expr, $($more:tt)+) => {
62 $crate::definable_name_map_internal!($map, .. $value);
63 $crate::definable_name_map_internal!($map, $($more)+);
64 };
65 ($map:ident, typeof $($name:ident).+ = $value:expr $(,)?) => {
67 $map.insert(
68 $crate::definable_name_map_pattern_internal!($($name).+ typeof).into(),
69 $value.into()
70 );
71 };
72 ($map:ident, $($name:ident).+ = $value:expr $(,)?) => {
73 $map.insert(
74 $crate::definable_name_map_pattern_internal!($($name).+).into(),
75 $value.into()
76 );
77 };
78 ($map:ident, typeof $($name:ident).+ = $value:expr, $($more:tt)+) => {
80 $crate::definable_name_map_internal!($map, typeof $($name).+ = $value);
81 $crate::definable_name_map_internal!($map, $($more)+);
82 };
83 ($map:ident, $($name:ident).+ = $value:expr, $($more:tt)+) => {
84 $crate::definable_name_map_internal!($map, $($name).+ = $value);
85 $crate::definable_name_map_internal!($map, $($more)+);
86 };
87
88}
89
90#[macro_export]
91macro_rules! compile_time_defines {
92 ($($more:tt)+) => {
93 {
94 let mut map = $crate::__private::FxIndexMap::default();
95 $crate::definable_name_map_internal!(map, $($more)+);
96 $crate::compile_time_info::CompileTimeDefines(map)
97 }
98 };
99}
100
101#[macro_export]
102macro_rules! free_var_references {
103 ($($more:tt)+) => {
104 {
105 let mut map = $crate::__private::FxIndexMap::default();
106 $crate::definable_name_map_internal!(map, $($more)+);
107 $crate::compile_time_info::FreeVarReferences(map)
108 }
109 };
110}
111
112#[derive(Debug, Clone, Hash, TraceRawVcs, NonLocalValue, Encode, Decode, PartialEq, Eq)]
115pub enum CompileTimeDefineValue {
116 Null,
117 Bool(bool),
118 Number(TotalOrderF64),
119 String(RcStr),
120 BigInt(
121 #[bincode(with_serde)]
122 #[turbo_tasks(trace_ignore)]
123 Box<BigInt>,
124 ),
125 Array(Vec<CompileTimeDefineValue>),
126 Object(Vec<(RcStr, CompileTimeDefineValue)>),
127 Undefined,
128 Evaluate(RcStr),
129 Regex(RcStr, RcStr),
130}
131
132#[derive(Debug, Copy, Clone, TraceRawVcs, NonLocalValue, Encode, Decode)]
134pub struct TotalOrderF64(f64);
135impl PartialEq for TotalOrderF64 {
136 fn eq(&self, other: &Self) -> bool {
137 self.0.total_cmp(&other.0) == std::cmp::Ordering::Equal
138 }
139}
140impl Eq for TotalOrderF64 {}
141impl Hash for TotalOrderF64 {
142 fn hash<H: Hasher>(&self, state: &mut H) {
143 self.0.to_le_bytes().hash(state);
144 }
145}
146impl From<f64> for TotalOrderF64 {
147 fn from(value: f64) -> Self {
148 Self(value)
149 }
150}
151impl Deref for TotalOrderF64 {
152 type Target = f64;
153 fn deref(&self) -> &Self::Target {
154 &self.0
155 }
156}
157impl Display for TotalOrderF64 {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 self.0.fmt(f)
160 }
161}
162
163impl From<bool> for CompileTimeDefineValue {
164 fn from(value: bool) -> Self {
165 Self::Bool(value)
166 }
167}
168
169impl From<RcStr> for CompileTimeDefineValue {
170 fn from(value: RcStr) -> Self {
171 Self::String(value)
172 }
173}
174
175impl From<String> for CompileTimeDefineValue {
176 fn from(value: String) -> Self {
177 Self::String(value.into())
178 }
179}
180
181impl From<&str> for CompileTimeDefineValue {
182 fn from(value: &str) -> Self {
183 Self::String(value.into())
184 }
185}
186
187impl From<serde_json::Value> for CompileTimeDefineValue {
188 fn from(value: serde_json::Value) -> Self {
189 match value {
190 serde_json::Value::Null => Self::Null,
191 serde_json::Value::Bool(b) => Self::Bool(b),
192 serde_json::Value::Number(n) => Self::Number(
193 n.as_f64()
194 .expect("unreachable: serde-json has arbitrary_precision disabled")
195 .into(),
196 ),
197 serde_json::Value::String(s) => Self::String(s.into()),
198 serde_json::Value::Array(a) => Self::Array(a.into_iter().map(|i| i.into()).collect()),
199 serde_json::Value::Object(m) => {
200 Self::Object(m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
201 }
202 }
203 }
204}
205
206#[turbo_tasks::value]
207#[derive(Debug, Clone, PartialOrd, Ord)]
208pub enum DefinableNameSegment {
209 Name(RcStr),
210 Call(RcStr),
211 TypeOf,
212}
213
214impl std::hash::Hash for DefinableNameSegment {
220 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
221 match self {
222 Self::Name(s) => {
223 0u8.hash(state);
224 s.as_str().hash(state);
225 }
226 Self::Call(s) => {
227 1u8.hash(state);
228 s.as_str().hash(state);
229 }
230 Self::TypeOf => {
231 2u8.hash(state);
232 }
233 }
234 }
235}
236
237impl From<RcStr> for DefinableNameSegment {
238 fn from(value: RcStr) -> Self {
239 DefinableNameSegment::Name(value)
240 }
241}
242
243impl From<&str> for DefinableNameSegment {
244 fn from(value: &str) -> Self {
245 DefinableNameSegment::Name(value.into())
246 }
247}
248
249impl From<String> for DefinableNameSegment {
250 fn from(value: String) -> Self {
251 DefinableNameSegment::Name(value.into())
252 }
253}
254
255#[derive(PartialEq, Eq)]
256pub enum DefinableNameSegmentRef<'a> {
257 Name(&'a str),
258 Call(&'a str),
259 TypeOf,
260}
261
262impl std::hash::Hash for DefinableNameSegmentRef<'_> {
266 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
267 match self {
268 Self::Name(s) => {
269 0u8.hash(state);
270 s.hash(state);
271 }
272 Self::Call(s) => {
273 1u8.hash(state);
274 s.hash(state);
275 }
276 Self::TypeOf => {
277 2u8.hash(state);
278 }
279 }
280 }
281}
282
283impl Equivalent<DefinableNameSegment> for DefinableNameSegmentRef<'_> {
284 fn equivalent(&self, key: &DefinableNameSegment) -> bool {
285 match (self, key) {
286 (DefinableNameSegmentRef::Name(a), DefinableNameSegment::Name(b)) => **a == *b.as_str(),
287 (DefinableNameSegmentRef::Call(a), DefinableNameSegment::Call(b)) => **a == *b.as_str(),
288 (DefinableNameSegmentRef::TypeOf, DefinableNameSegment::TypeOf) => true,
289 _ => false,
290 }
291 }
292}
293
294#[derive(PartialEq, Eq)]
295pub struct DefinableNameSegmentRefs<'a>(pub SmallVec<[DefinableNameSegmentRef<'a>; 4]>);
296
297impl std::hash::Hash for DefinableNameSegmentRefs<'_> {
299 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
300 self.0.len().hash(state);
301 for segment in &self.0 {
302 segment.hash(state);
303 }
304 }
305}
306
307impl Equivalent<Vec<DefinableNameSegment>> for DefinableNameSegmentRefs<'_> {
308 fn equivalent(&self, key: &Vec<DefinableNameSegment>) -> bool {
309 if self.0.len() != key.len() {
310 return false;
311 }
312 for (a, b) in self.0.iter().zip(key.iter()) {
313 if !a.equivalent(b) {
314 return false;
315 }
316 }
317 true
318 }
319}
320
321#[turbo_tasks::value(transparent, cell = "keyed")]
322#[derive(Debug, Clone)]
323pub struct CompileTimeDefines(
324 #[bincode(with = "turbo_bincode::indexmap")]
325 pub FxIndexMap<Vec<DefinableNameSegment>, CompileTimeDefineValue>,
326);
327
328impl IntoIterator for CompileTimeDefines {
329 type Item = (Vec<DefinableNameSegment>, CompileTimeDefineValue);
330 type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, CompileTimeDefineValue>;
331
332 fn into_iter(self) -> Self::IntoIter {
333 self.0.into_iter()
334 }
335}
336
337#[turbo_tasks::value_impl]
338impl CompileTimeDefines {
339 #[turbo_tasks::function]
340 pub fn empty() -> Vc<Self> {
341 Vc::cell(FxIndexMap::default())
342 }
343
344 #[turbo_tasks::function]
345 pub async fn read_process_env(&self, key: RcStr) -> Result<Vc<Option<RcStr>>> {
346 let key = DefinableNameSegmentRefs(smallvec![
347 DefinableNameSegmentRef::Name("process"),
348 DefinableNameSegmentRef::Name("env"),
349 DefinableNameSegmentRef::Name(&key),
350 ]);
351 Ok(Vc::cell(match self.0.get(&key) {
352 Some(CompileTimeDefineValue::String(s)) => Some(s.clone()),
353 _ => None,
354 }))
355 }
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
359pub enum InputRelativeConstant {
360 DirName,
362 FileName,
364}
365
366#[derive(Debug, Clone, TraceRawVcs, NonLocalValue, Encode, Decode, PartialEq, Eq)]
367pub enum FreeVarReference {
368 EcmaScriptModule {
369 request: RcStr,
370 lookup_path: Option<FileSystemPath>,
371 export: Option<RcStr>,
372 },
373 Ident(RcStr),
374 Member(RcStr, RcStr),
375 Value(CompileTimeDefineValue),
376 InputRelative(InputRelativeConstant),
377 ReportUsage {
380 message: RcStr,
381 severity: IssueSeverity,
382 inner: Option<Box<FreeVarReference>>,
383 },
384}
385
386impl From<bool> for FreeVarReference {
387 fn from(value: bool) -> Self {
388 Self::Value(value.into())
389 }
390}
391
392impl From<String> for FreeVarReference {
393 fn from(value: String) -> Self {
394 Self::Value(value.into())
395 }
396}
397impl From<RcStr> for FreeVarReference {
398 fn from(value: RcStr) -> Self {
399 Self::Value(value.into())
400 }
401}
402
403impl From<&str> for FreeVarReference {
404 fn from(value: &str) -> Self {
405 Self::Value(value.into())
406 }
407}
408
409impl From<CompileTimeDefineValue> for FreeVarReference {
410 fn from(value: CompileTimeDefineValue) -> Self {
411 Self::Value(value)
412 }
413}
414
415#[turbo_tasks::value(transparent, cell = "keyed")]
416#[derive(Debug, Clone)]
417pub struct FreeVarReferences(
418 #[bincode(with = "turbo_bincode::indexmap")]
419 pub FxIndexMap<Vec<DefinableNameSegment>, FreeVarReference>,
420);
421
422#[turbo_tasks::value(transparent, cell = "keyed")]
423pub struct FreeVarReferencesMembers(FxHashSet<RcStr>);
424
425#[turbo_tasks::value_impl]
426impl FreeVarReferences {
427 #[turbo_tasks::function]
428 pub fn empty() -> Vc<Self> {
429 Vc::cell(FxIndexMap::default())
430 }
431
432 #[turbo_tasks::function]
433 pub fn members(&self) -> Vc<FreeVarReferencesMembers> {
434 let mut members = FxHashSet::default();
435 for (key, _) in self.0.iter() {
436 if let Some(name) = key
437 .iter()
438 .rfind(|segment| {
439 matches!(
440 segment,
441 DefinableNameSegment::Name(_) | DefinableNameSegment::Call(_)
442 )
443 })
444 .and_then(|segment| match segment {
445 DefinableNameSegment::Name(n) | DefinableNameSegment::Call(n) => Some(n),
446 _ => None,
447 })
448 {
449 members.insert(name.clone());
450 }
451 }
452 Vc::cell(members)
453 }
454}
455
456impl IntoIterator for FreeVarReferences {
457 type Item = (Vec<DefinableNameSegment>, FreeVarReference);
458 type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, FreeVarReference>;
459
460 fn into_iter(self) -> Self::IntoIter {
461 self.0.into_iter()
462 }
463}
464
465#[turbo_tasks::value(shared)]
466#[derive(Debug, Clone)]
467pub struct CompileTimeInfo {
468 pub environment: ResolvedVc<Environment>,
469 pub defines: ResolvedVc<CompileTimeDefines>,
470 pub free_var_references: ResolvedVc<FreeVarReferences>,
471 pub hot_module_replacement_enabled: bool,
472}
473
474impl CompileTimeInfo {
475 pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
476 CompileTimeInfoBuilder {
477 environment,
478 defines: None,
479 free_var_references: None,
480 hot_module_replacement_enabled: false,
481 }
482 }
483}
484
485#[turbo_tasks::value_impl]
486impl CompileTimeInfo {
487 #[turbo_tasks::function]
488 pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
489 Ok(CompileTimeInfo {
490 environment,
491 defines: CompileTimeDefines::empty().to_resolved().await?,
492 free_var_references: FreeVarReferences::empty().to_resolved().await?,
493 hot_module_replacement_enabled: false,
494 }
495 .cell())
496 }
497
498 #[turbo_tasks::function]
499 pub fn environment(&self) -> Vc<Environment> {
500 *self.environment
501 }
502}
503
504pub struct CompileTimeInfoBuilder {
505 environment: ResolvedVc<Environment>,
506 defines: Option<ResolvedVc<CompileTimeDefines>>,
507 free_var_references: Option<ResolvedVc<FreeVarReferences>>,
508 hot_module_replacement_enabled: bool,
509}
510
511impl CompileTimeInfoBuilder {
512 pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
513 self.defines = Some(defines);
514 self
515 }
516
517 pub fn free_var_references(
518 mut self,
519 free_var_references: ResolvedVc<FreeVarReferences>,
520 ) -> Self {
521 self.free_var_references = Some(free_var_references);
522 self
523 }
524
525 pub fn hot_module_replacement_enabled(mut self, enabled: bool) -> Self {
526 self.hot_module_replacement_enabled = enabled;
527 self
528 }
529
530 pub async fn build(self) -> Result<CompileTimeInfo> {
531 Ok(CompileTimeInfo {
532 environment: self.environment,
533 defines: match self.defines {
534 Some(defines) => defines,
535 None => CompileTimeDefines::empty().to_resolved().await?,
536 },
537 free_var_references: match self.free_var_references {
538 Some(free_var_references) => free_var_references,
539 None => FreeVarReferences::empty().to_resolved().await?,
540 },
541 hot_module_replacement_enabled: self.hot_module_replacement_enabled,
542 })
543 }
544
545 pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
546 Ok(self.build().await?.cell())
547 }
548}
549
550#[cfg(test)]
551mod test {
552 use std::{
553 collections::hash_map::DefaultHasher,
554 hash::{Hash, Hasher},
555 };
556
557 use smallvec::smallvec;
558 use turbo_rcstr::rcstr;
559 use turbo_tasks::FxIndexMap;
560
561 use crate::compile_time_info::{
562 DefinableNameSegment, DefinableNameSegmentRef, DefinableNameSegmentRefs, FreeVarReference,
563 FreeVarReferences,
564 };
565
566 fn hash_value<T: Hash>(value: &T) -> u64 {
567 let mut hasher = DefaultHasher::new();
568 value.hash(&mut hasher);
569 hasher.finish()
570 }
571
572 #[test]
573 fn hash_segment_name_matches() {
574 let segment = DefinableNameSegment::Name(rcstr!("process"));
575 let segment_ref = DefinableNameSegmentRef::Name("process");
576 assert_eq!(
577 hash_value(&segment),
578 hash_value(&segment_ref),
579 "DefinableNameSegment::Name and DefinableNameSegmentRef::Name must have matching Hash"
580 );
581 }
582
583 #[test]
584 fn hash_segment_call_matches() {
585 let segment = DefinableNameSegment::Call(rcstr!("foo"));
586 let segment_ref = DefinableNameSegmentRef::Call("foo");
587 assert_eq!(
588 hash_value(&segment),
589 hash_value(&segment_ref),
590 "DefinableNameSegment::Call and DefinableNameSegmentRef::Call must have matching Hash"
591 );
592 }
593
594 #[test]
595 fn hash_segment_typeof_matches() {
596 let segment = DefinableNameSegment::TypeOf;
597 let segment_ref = DefinableNameSegmentRef::TypeOf;
598 assert_eq!(
599 hash_value(&segment),
600 hash_value(&segment_ref),
601 "DefinableNameSegment::TypeOf and DefinableNameSegmentRef::TypeOf must have matching \
602 Hash"
603 );
604 }
605
606 #[test]
607 fn hash_segments_vec_matches() {
608 let segments: Vec<DefinableNameSegment> = vec![
609 DefinableNameSegment::Name(rcstr!("process")),
610 DefinableNameSegment::Name(rcstr!("env")),
611 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
612 ];
613 let segments_ref = DefinableNameSegmentRefs(smallvec![
614 DefinableNameSegmentRef::Name("process"),
615 DefinableNameSegmentRef::Name("env"),
616 DefinableNameSegmentRef::Name("NODE_ENV"),
617 ]);
618 assert_eq!(
619 hash_value(&segments),
620 hash_value(&segments_ref),
621 "Vec<DefinableNameSegment> and DefinableNameSegmentRefs must have matching Hash"
622 );
623 }
624
625 #[test]
626 fn hash_segments_with_typeof_matches() {
627 let segments: Vec<DefinableNameSegment> = vec![
628 DefinableNameSegment::Name(rcstr!("process")),
629 DefinableNameSegment::TypeOf,
630 ];
631 let segments_ref = DefinableNameSegmentRefs(smallvec![
632 DefinableNameSegmentRef::Name("process"),
633 DefinableNameSegmentRef::TypeOf,
634 ]);
635 assert_eq!(
636 hash_value(&segments),
637 hash_value(&segments_ref),
638 "Vec<DefinableNameSegment> with TypeOf and DefinableNameSegmentRefs must have \
639 matching Hash"
640 );
641 }
642
643 #[test]
644 fn hash_segments_with_call_matches() {
645 let segments: Vec<DefinableNameSegment> = vec![
646 DefinableNameSegment::Name(rcstr!("foo")),
647 DefinableNameSegment::Call(rcstr!("bar")),
648 ];
649 let segments_ref = DefinableNameSegmentRefs(smallvec![
650 DefinableNameSegmentRef::Name("foo"),
651 DefinableNameSegmentRef::Call("bar"),
652 ]);
653 assert_eq!(
654 hash_value(&segments),
655 hash_value(&segments_ref),
656 "Vec<DefinableNameSegment> with Call and DefinableNameSegmentRefs must have matching \
657 Hash"
658 );
659 }
660
661 #[test]
662 fn macro_parser() {
663 assert_eq!(
664 free_var_references!(
665 FOO = "bar",
666 FOO = false,
667 Buffer = FreeVarReference::EcmaScriptModule {
668 request: rcstr!("node:buffer"),
669 lookup_path: None,
670 export: Some(rcstr!("Buffer")),
671 },
672 ),
673 FreeVarReferences(FxIndexMap::from_iter(vec![
674 (
675 vec![rcstr!("FOO").into()],
676 FreeVarReference::Value(rcstr!("bar").into())
677 ),
678 (
679 vec![rcstr!("FOO").into()],
680 FreeVarReference::Value(false.into())
681 ),
682 (
683 vec![rcstr!("Buffer").into()],
684 FreeVarReference::EcmaScriptModule {
685 request: rcstr!("node:buffer"),
686 lookup_path: None,
687 export: Some(rcstr!("Buffer")),
688 }
689 ),
690 ]))
691 );
692 }
693
694 #[test]
695 fn macro_parser_typeof() {
696 assert_eq!(
697 free_var_references!(
698 typeof x = "a",
699 typeof x.y = "b",
700 typeof x.y.z = "c"
701 ),
702 FreeVarReferences(FxIndexMap::from_iter(vec![
703 (
704 vec![rcstr!("x").into(), DefinableNameSegment::TypeOf],
705 FreeVarReference::Value(rcstr!("a").into())
706 ),
707 (
708 vec![
709 rcstr!("x").into(),
710 rcstr!("y").into(),
711 DefinableNameSegment::TypeOf
712 ],
713 FreeVarReference::Value(rcstr!("b").into())
714 ),
715 (
716 vec![
717 rcstr!("x").into(),
718 rcstr!("y").into(),
719 rcstr!("z").into(),
720 DefinableNameSegment::TypeOf
721 ],
722 FreeVarReference::Value(rcstr!("b").into())
723 ),
724 (
725 vec![
726 rcstr!("x").into(),
727 rcstr!("y").into(),
728 rcstr!("z").into(),
729 DefinableNameSegment::TypeOf
730 ],
731 FreeVarReference::Value(rcstr!("c").into())
732 )
733 ]))
734 );
735 }
736
737 #[test]
738 fn indexmap_lookup_with_equivalent() {
739 let mut map: FxIndexMap<Vec<DefinableNameSegment>, &str> = FxIndexMap::default();
742 map.insert(
743 vec![
744 DefinableNameSegment::Name(rcstr!("process")),
745 DefinableNameSegment::Name(rcstr!("env")),
746 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
747 ],
748 "production",
749 );
750 map.insert(
751 vec![
752 DefinableNameSegment::Name(rcstr!("process")),
753 DefinableNameSegment::Name(rcstr!("turbopack")),
754 ],
755 "true",
756 );
757
758 let key = DefinableNameSegmentRefs(smallvec![
760 DefinableNameSegmentRef::Name("process"),
761 DefinableNameSegmentRef::Name("env"),
762 DefinableNameSegmentRef::Name("NODE_ENV"),
763 ]);
764 assert_eq!(
765 map.get(&key),
766 Some(&"production"),
767 "IndexMap lookup with Equivalent trait should work"
768 );
769
770 let key2 = DefinableNameSegmentRefs(smallvec![
771 DefinableNameSegmentRef::Name("process"),
772 DefinableNameSegmentRef::Name("turbopack"),
773 ]);
774 assert_eq!(
775 map.get(&key2),
776 Some(&"true"),
777 "IndexMap lookup with Equivalent trait should work for shorter keys"
778 );
779
780 let key3 = DefinableNameSegmentRefs(smallvec![
781 DefinableNameSegmentRef::Name("process"),
782 DefinableNameSegmentRef::Name("nonexistent"),
783 ]);
784 assert_eq!(
785 map.get(&key3),
786 None,
787 "IndexMap lookup should return None for nonexistent keys"
788 );
789 }
790
791 #[test]
792 fn fxhashset_rcstr_lookup_with_str() {
793 use rustc_hash::FxHashSet;
796
797 let mut set: FxHashSet<turbo_rcstr::RcStr> = FxHashSet::default();
798 set.insert(rcstr!("process"));
799 set.insert(rcstr!("env"));
800 set.insert(rcstr!("NODE_ENV"));
801
802 assert!(
805 set.contains("process"),
806 "FxHashSet<RcStr> lookup with &str should work for 'process'"
807 );
808 assert!(
809 set.contains("env"),
810 "FxHashSet<RcStr> lookup with &str should work for 'env'"
811 );
812 assert!(
813 set.contains("NODE_ENV"),
814 "FxHashSet<RcStr> lookup with &str should work for 'NODE_ENV'"
815 );
816 assert!(
817 !set.contains("nonexistent"),
818 "FxHashSet<RcStr> lookup with &str should return false for nonexistent keys"
819 );
820 }
821}