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;
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
345#[derive(Debug, Clone, Copy, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
346pub enum InputRelativeConstant {
347 DirName,
349 FileName,
351}
352
353#[derive(Debug, Clone, TraceRawVcs, NonLocalValue, Encode, Decode, PartialEq, Eq)]
354pub enum FreeVarReference {
355 EcmaScriptModule {
356 request: RcStr,
357 lookup_path: Option<FileSystemPath>,
358 export: Option<RcStr>,
359 },
360 Ident(RcStr),
361 Member(RcStr, RcStr),
362 Value(CompileTimeDefineValue),
363 InputRelative(InputRelativeConstant),
364 ReportUsage {
367 message: RcStr,
368 severity: IssueSeverity,
369 inner: Option<Box<FreeVarReference>>,
370 },
371}
372
373impl From<bool> for FreeVarReference {
374 fn from(value: bool) -> Self {
375 Self::Value(value.into())
376 }
377}
378
379impl From<String> for FreeVarReference {
380 fn from(value: String) -> Self {
381 Self::Value(value.into())
382 }
383}
384impl From<RcStr> for FreeVarReference {
385 fn from(value: RcStr) -> Self {
386 Self::Value(value.into())
387 }
388}
389
390impl From<&str> for FreeVarReference {
391 fn from(value: &str) -> Self {
392 Self::Value(value.into())
393 }
394}
395
396impl From<CompileTimeDefineValue> for FreeVarReference {
397 fn from(value: CompileTimeDefineValue) -> Self {
398 Self::Value(value)
399 }
400}
401
402#[turbo_tasks::value(transparent, cell = "keyed")]
403#[derive(Debug, Clone)]
404pub struct FreeVarReferences(
405 #[bincode(with = "turbo_bincode::indexmap")]
406 pub FxIndexMap<Vec<DefinableNameSegment>, FreeVarReference>,
407);
408
409#[turbo_tasks::value(transparent, cell = "keyed")]
410pub struct FreeVarReferencesMembers(FxHashSet<RcStr>);
411
412#[turbo_tasks::value_impl]
413impl FreeVarReferences {
414 #[turbo_tasks::function]
415 pub fn empty() -> Vc<Self> {
416 Vc::cell(FxIndexMap::default())
417 }
418
419 #[turbo_tasks::function]
420 pub fn members(&self) -> Vc<FreeVarReferencesMembers> {
421 let mut members = FxHashSet::default();
422 for (key, _) in self.0.iter() {
423 if let Some(name) = key
424 .iter()
425 .rfind(|segment| {
426 matches!(
427 segment,
428 DefinableNameSegment::Name(_) | DefinableNameSegment::Call(_)
429 )
430 })
431 .and_then(|segment| match segment {
432 DefinableNameSegment::Name(n) | DefinableNameSegment::Call(n) => Some(n),
433 _ => None,
434 })
435 {
436 members.insert(name.clone());
437 }
438 }
439 Vc::cell(members)
440 }
441}
442
443impl IntoIterator for FreeVarReferences {
444 type Item = (Vec<DefinableNameSegment>, FreeVarReference);
445 type IntoIter = indexmap::map::IntoIter<Vec<DefinableNameSegment>, FreeVarReference>;
446
447 fn into_iter(self) -> Self::IntoIter {
448 self.0.into_iter()
449 }
450}
451
452#[turbo_tasks::value(shared)]
453#[derive(Debug, Clone)]
454pub struct CompileTimeInfo {
455 pub environment: ResolvedVc<Environment>,
456 pub defines: ResolvedVc<CompileTimeDefines>,
457 pub free_var_references: ResolvedVc<FreeVarReferences>,
458 pub hot_module_replacement_enabled: bool,
459}
460
461impl CompileTimeInfo {
462 pub fn builder(environment: ResolvedVc<Environment>) -> CompileTimeInfoBuilder {
463 CompileTimeInfoBuilder {
464 environment,
465 defines: None,
466 free_var_references: None,
467 hot_module_replacement_enabled: false,
468 }
469 }
470}
471
472#[turbo_tasks::value_impl]
473impl CompileTimeInfo {
474 #[turbo_tasks::function]
475 pub async fn new(environment: ResolvedVc<Environment>) -> Result<Vc<Self>> {
476 Ok(CompileTimeInfo {
477 environment,
478 defines: CompileTimeDefines::empty().to_resolved().await?,
479 free_var_references: FreeVarReferences::empty().to_resolved().await?,
480 hot_module_replacement_enabled: false,
481 }
482 .cell())
483 }
484
485 #[turbo_tasks::function]
486 pub fn environment(&self) -> Vc<Environment> {
487 *self.environment
488 }
489}
490
491pub struct CompileTimeInfoBuilder {
492 environment: ResolvedVc<Environment>,
493 defines: Option<ResolvedVc<CompileTimeDefines>>,
494 free_var_references: Option<ResolvedVc<FreeVarReferences>>,
495 hot_module_replacement_enabled: bool,
496}
497
498impl CompileTimeInfoBuilder {
499 pub fn defines(mut self, defines: ResolvedVc<CompileTimeDefines>) -> Self {
500 self.defines = Some(defines);
501 self
502 }
503
504 pub fn free_var_references(
505 mut self,
506 free_var_references: ResolvedVc<FreeVarReferences>,
507 ) -> Self {
508 self.free_var_references = Some(free_var_references);
509 self
510 }
511
512 pub fn hot_module_replacement_enabled(mut self, enabled: bool) -> Self {
513 self.hot_module_replacement_enabled = enabled;
514 self
515 }
516
517 pub async fn build(self) -> Result<CompileTimeInfo> {
518 Ok(CompileTimeInfo {
519 environment: self.environment,
520 defines: match self.defines {
521 Some(defines) => defines,
522 None => CompileTimeDefines::empty().to_resolved().await?,
523 },
524 free_var_references: match self.free_var_references {
525 Some(free_var_references) => free_var_references,
526 None => FreeVarReferences::empty().to_resolved().await?,
527 },
528 hot_module_replacement_enabled: self.hot_module_replacement_enabled,
529 })
530 }
531
532 pub async fn cell(self) -> Result<Vc<CompileTimeInfo>> {
533 Ok(self.build().await?.cell())
534 }
535}
536
537#[cfg(test)]
538mod test {
539 use std::{
540 collections::hash_map::DefaultHasher,
541 hash::{Hash, Hasher},
542 };
543
544 use smallvec::smallvec;
545 use turbo_rcstr::rcstr;
546 use turbo_tasks::FxIndexMap;
547
548 use crate::compile_time_info::{
549 DefinableNameSegment, DefinableNameSegmentRef, DefinableNameSegmentRefs, FreeVarReference,
550 FreeVarReferences,
551 };
552
553 fn hash_value<T: Hash>(value: &T) -> u64 {
554 let mut hasher = DefaultHasher::new();
555 value.hash(&mut hasher);
556 hasher.finish()
557 }
558
559 #[test]
560 fn hash_segment_name_matches() {
561 let segment = DefinableNameSegment::Name(rcstr!("process"));
562 let segment_ref = DefinableNameSegmentRef::Name("process");
563 assert_eq!(
564 hash_value(&segment),
565 hash_value(&segment_ref),
566 "DefinableNameSegment::Name and DefinableNameSegmentRef::Name must have matching Hash"
567 );
568 }
569
570 #[test]
571 fn hash_segment_call_matches() {
572 let segment = DefinableNameSegment::Call(rcstr!("foo"));
573 let segment_ref = DefinableNameSegmentRef::Call("foo");
574 assert_eq!(
575 hash_value(&segment),
576 hash_value(&segment_ref),
577 "DefinableNameSegment::Call and DefinableNameSegmentRef::Call must have matching Hash"
578 );
579 }
580
581 #[test]
582 fn hash_segment_typeof_matches() {
583 let segment = DefinableNameSegment::TypeOf;
584 let segment_ref = DefinableNameSegmentRef::TypeOf;
585 assert_eq!(
586 hash_value(&segment),
587 hash_value(&segment_ref),
588 "DefinableNameSegment::TypeOf and DefinableNameSegmentRef::TypeOf must have matching \
589 Hash"
590 );
591 }
592
593 #[test]
594 fn hash_segments_vec_matches() {
595 let segments: Vec<DefinableNameSegment> = vec![
596 DefinableNameSegment::Name(rcstr!("process")),
597 DefinableNameSegment::Name(rcstr!("env")),
598 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
599 ];
600 let segments_ref = DefinableNameSegmentRefs(smallvec![
601 DefinableNameSegmentRef::Name("process"),
602 DefinableNameSegmentRef::Name("env"),
603 DefinableNameSegmentRef::Name("NODE_ENV"),
604 ]);
605 assert_eq!(
606 hash_value(&segments),
607 hash_value(&segments_ref),
608 "Vec<DefinableNameSegment> and DefinableNameSegmentRefs must have matching Hash"
609 );
610 }
611
612 #[test]
613 fn hash_segments_with_typeof_matches() {
614 let segments: Vec<DefinableNameSegment> = vec![
615 DefinableNameSegment::Name(rcstr!("process")),
616 DefinableNameSegment::TypeOf,
617 ];
618 let segments_ref = DefinableNameSegmentRefs(smallvec![
619 DefinableNameSegmentRef::Name("process"),
620 DefinableNameSegmentRef::TypeOf,
621 ]);
622 assert_eq!(
623 hash_value(&segments),
624 hash_value(&segments_ref),
625 "Vec<DefinableNameSegment> with TypeOf and DefinableNameSegmentRefs must have \
626 matching Hash"
627 );
628 }
629
630 #[test]
631 fn hash_segments_with_call_matches() {
632 let segments: Vec<DefinableNameSegment> = vec![
633 DefinableNameSegment::Name(rcstr!("foo")),
634 DefinableNameSegment::Call(rcstr!("bar")),
635 ];
636 let segments_ref = DefinableNameSegmentRefs(smallvec![
637 DefinableNameSegmentRef::Name("foo"),
638 DefinableNameSegmentRef::Call("bar"),
639 ]);
640 assert_eq!(
641 hash_value(&segments),
642 hash_value(&segments_ref),
643 "Vec<DefinableNameSegment> with Call and DefinableNameSegmentRefs must have matching \
644 Hash"
645 );
646 }
647
648 #[test]
649 fn macro_parser() {
650 assert_eq!(
651 free_var_references!(
652 FOO = "bar",
653 FOO = false,
654 Buffer = FreeVarReference::EcmaScriptModule {
655 request: rcstr!("node:buffer"),
656 lookup_path: None,
657 export: Some(rcstr!("Buffer")),
658 },
659 ),
660 FreeVarReferences(FxIndexMap::from_iter(vec![
661 (
662 vec![rcstr!("FOO").into()],
663 FreeVarReference::Value(rcstr!("bar").into())
664 ),
665 (
666 vec![rcstr!("FOO").into()],
667 FreeVarReference::Value(false.into())
668 ),
669 (
670 vec![rcstr!("Buffer").into()],
671 FreeVarReference::EcmaScriptModule {
672 request: rcstr!("node:buffer"),
673 lookup_path: None,
674 export: Some(rcstr!("Buffer")),
675 }
676 ),
677 ]))
678 );
679 }
680
681 #[test]
682 fn macro_parser_typeof() {
683 assert_eq!(
684 free_var_references!(
685 typeof x = "a",
686 typeof x.y = "b",
687 typeof x.y.z = "c"
688 ),
689 FreeVarReferences(FxIndexMap::from_iter(vec![
690 (
691 vec![rcstr!("x").into(), DefinableNameSegment::TypeOf],
692 FreeVarReference::Value(rcstr!("a").into())
693 ),
694 (
695 vec![
696 rcstr!("x").into(),
697 rcstr!("y").into(),
698 DefinableNameSegment::TypeOf
699 ],
700 FreeVarReference::Value(rcstr!("b").into())
701 ),
702 (
703 vec![
704 rcstr!("x").into(),
705 rcstr!("y").into(),
706 rcstr!("z").into(),
707 DefinableNameSegment::TypeOf
708 ],
709 FreeVarReference::Value(rcstr!("b").into())
710 ),
711 (
712 vec![
713 rcstr!("x").into(),
714 rcstr!("y").into(),
715 rcstr!("z").into(),
716 DefinableNameSegment::TypeOf
717 ],
718 FreeVarReference::Value(rcstr!("c").into())
719 )
720 ]))
721 );
722 }
723
724 #[test]
725 fn indexmap_lookup_with_equivalent() {
726 let mut map: FxIndexMap<Vec<DefinableNameSegment>, &str> = FxIndexMap::default();
729 map.insert(
730 vec![
731 DefinableNameSegment::Name(rcstr!("process")),
732 DefinableNameSegment::Name(rcstr!("env")),
733 DefinableNameSegment::Name(rcstr!("NODE_ENV")),
734 ],
735 "production",
736 );
737 map.insert(
738 vec![
739 DefinableNameSegment::Name(rcstr!("process")),
740 DefinableNameSegment::Name(rcstr!("turbopack")),
741 ],
742 "true",
743 );
744
745 let key = DefinableNameSegmentRefs(smallvec![
747 DefinableNameSegmentRef::Name("process"),
748 DefinableNameSegmentRef::Name("env"),
749 DefinableNameSegmentRef::Name("NODE_ENV"),
750 ]);
751 assert_eq!(
752 map.get(&key),
753 Some(&"production"),
754 "IndexMap lookup with Equivalent trait should work"
755 );
756
757 let key2 = DefinableNameSegmentRefs(smallvec![
758 DefinableNameSegmentRef::Name("process"),
759 DefinableNameSegmentRef::Name("turbopack"),
760 ]);
761 assert_eq!(
762 map.get(&key2),
763 Some(&"true"),
764 "IndexMap lookup with Equivalent trait should work for shorter keys"
765 );
766
767 let key3 = DefinableNameSegmentRefs(smallvec![
768 DefinableNameSegmentRef::Name("process"),
769 DefinableNameSegmentRef::Name("nonexistent"),
770 ]);
771 assert_eq!(
772 map.get(&key3),
773 None,
774 "IndexMap lookup should return None for nonexistent keys"
775 );
776 }
777
778 #[test]
779 fn fxhashset_rcstr_lookup_with_str() {
780 use rustc_hash::FxHashSet;
783
784 let mut set: FxHashSet<turbo_rcstr::RcStr> = FxHashSet::default();
785 set.insert(rcstr!("process"));
786 set.insert(rcstr!("env"));
787 set.insert(rcstr!("NODE_ENV"));
788
789 assert!(
792 set.contains("process"),
793 "FxHashSet<RcStr> lookup with &str should work for 'process'"
794 );
795 assert!(
796 set.contains("env"),
797 "FxHashSet<RcStr> lookup with &str should work for 'env'"
798 );
799 assert!(
800 set.contains("NODE_ENV"),
801 "FxHashSet<RcStr> lookup with &str should work for 'NODE_ENV'"
802 );
803 assert!(
804 !set.contains("nonexistent"),
805 "FxHashSet<RcStr> lookup with &str should return false for nonexistent keys"
806 );
807 }
808}