mezzaluna_load_path/
rubylib.rs

1use std::env;
2use std::ffi::OsStr;
3use std::path::PathBuf;
4
5/// A Ruby load path builder that reads from the `RUBYLIB` environment variable.
6///
7/// MRI Ruby allows manipulating the [require] search path by setting the
8/// `RUBYLIB` environment variable before launching the Ruby CLI. The `RUBYLIB`
9/// variable is read on start-up and is expected to contain a platform-native
10/// path separator-delimited list of file system paths.
11///
12/// The `RUBYLIB` environment variable or other sequence of paths is parsed when
13/// this loader is created and is immutable. This builder is intended to be
14/// called during interpreter boot.
15///
16/// Paths earlier in the sequence returned from [`load_path`] have higher
17/// priority.
18///
19/// ```no_run
20/// use std::ffi::OsStr;
21/// use std::path::Path;
22/// use mezzaluna_load_path::Rubylib;
23///
24/// # #[cfg(unix)]
25/// # fn example() -> Option<()> {
26/// // Grab the load paths from the `RUBYLIB` environment variable. If the
27/// // variable is empty or unset, `None` is returned.
28/// let env_loader = Rubylib::new()?;
29///
30/// // Search `/home/artichoke/src` first, only attempting to search
31/// // `/usr/share/artichoke` if no file is found in `/home/artichoke/src`.
32/// let fixed_loader = Rubylib::with_rubylib(
33///     OsStr::new("/home/artichoke/src:/usr/share/artichoke:./_lib"),
34/// )?;
35/// # Some(())
36/// # }
37/// # #[cfg(unix)]
38/// # example().unwrap();
39/// ```
40///
41/// [require]: https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-require
42/// [`load_path`]: Self::load_path
43#[derive(Default, Debug, Clone, PartialEq, Eq)]
44#[cfg_attr(docsrs, doc(cfg(feature = "rubylib")))]
45pub struct Rubylib {
46    /// Fixed set of paths on the host file system to search for Ruby sources.
47    ///
48    /// These load paths are loaded once and are immutable once loaded.
49    load_path: Box<[PathBuf]>,
50}
51
52impl Rubylib {
53    /// Create a new load path builder that reads from the `RUBYLIB` environment
54    /// variable.
55    ///
56    /// The `RUBYLIB` environment variable is read only once at the time this
57    /// method is called. The resolved load path is immutable.
58    ///
59    /// This method returns [`None`] if there are errors resolving the
60    /// `RUBYLIB` environment variable, if the `RUBYLIB` environment variable is
61    /// not set, or if the given `RUBYLIB` environment variable only contains
62    /// empty paths.
63    ///
64    /// # Examples
65    ///
66    /// ```no_run
67    /// use mezzaluna_load_path::Rubylib;
68    ///
69    /// # fn example() -> Option<()> {
70    /// let loader = Rubylib::new()?;
71    /// # Some(())
72    /// # }
73    /// # example().unwrap();
74    /// ```
75    #[must_use]
76    pub fn new() -> Option<Self> {
77        let rubylib = env::var_os("RUBYLIB")?;
78        Self::with_rubylib(&rubylib)
79    }
80
81    /// Create a new load path builder that reads from the given [`OsStr`].
82    ///
83    /// The `rubylib` platform string given to this method is expected to be a
84    /// [path string] of file system paths that are delimited by the platform
85    /// path separator.
86    ///
87    /// The resolved load path is immutable.
88    ///
89    /// This method returns [`None`] if the given `rubylib` argument only
90    /// contains empty paths.
91    /// non-empty paths.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use std::ffi::OsStr;
97    /// use mezzaluna_load_path::Rubylib;
98    ///
99    /// # #[cfg(unix)]
100    /// # fn example() -> Option<()> {
101    /// let loader = Rubylib::with_rubylib(OsStr::new("/home/artichoke/src:/usr/share/artichoke:_lib"))?;
102    /// # Some(())
103    /// # }
104    /// # #[cfg(unix)]
105    /// # example().unwrap();
106    /// ```
107    ///
108    /// An empty path string returns [`None`].
109    ///
110    /// ```
111    /// use mezzaluna_load_path::Rubylib;
112    ///
113    /// let loader = Rubylib::with_rubylib("");
114    /// assert!(loader.is_none());
115    ///
116    /// # #[cfg(unix)]
117    /// let loader = Rubylib::with_rubylib("::::");
118    /// # #[cfg(unix)]
119    /// assert!(loader.is_none());
120    /// ```
121    ///
122    /// [path string]: env::split_paths
123    #[must_use]
124    pub fn with_rubylib<T: AsRef<OsStr>>(rubylib: T) -> Option<Self> {
125        let rubylib = rubylib.as_ref();
126        // Empty paths are filtered out of `RUBYLIB`.
127        //
128        // `std::env::split_paths` yields empty paths as of Rust 1.69.0.
129        // See: <https://github.com/rust-lang/rust/issues/111832>
130        let load_path = env::split_paths(rubylib)
131            .filter(|p| !p.as_os_str().is_empty())
132            .collect::<Box<[_]>>();
133
134        // If the `RUBYLIB` env variable is empty or otherwise results in no
135        // search paths being resolved, return `None` so the `Rubylib` loader is
136        // not used.
137        if load_path.is_empty() {
138            return None;
139        }
140
141        Some(Self { load_path })
142    }
143
144    /// Return a reference to the paths in `$LOAD_PATH` parsed by this builder.
145    ///
146    /// Because the paths in `RUBYLIB` have the highest priority when loading
147    /// features, the returned paths should appear first in `$LOAD_PATH`.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use std::ffi::OsStr;
153    /// use std::path::Path;
154    /// use mezzaluna_load_path::Rubylib;
155    ///
156    /// # #[cfg(unix)]
157    /// # fn example() -> Option<()> {
158    /// let loader = Rubylib::with_rubylib(
159    ///     OsStr::new("/home/artichoke/src:/usr/share/artichoke:_lib"),
160    /// )?;
161    /// assert_eq!(
162    ///     loader.load_path(),
163    ///     &[
164    ///         Path::new("/home/artichoke/src"),
165    ///         Path::new("/usr/share/artichoke"),
166    ///         Path::new("_lib"),
167    ///     ]
168    /// );
169    /// # Some(())
170    /// # }
171    /// # #[cfg(unix)]
172    /// # example().unwrap();
173    /// ```
174    #[inline]
175    #[must_use]
176    pub fn load_path(&self) -> &[PathBuf] {
177        &self.load_path
178    }
179
180    /// Consume this loader and return its `$LOAD_PATH`.
181    ///
182    /// Because the paths in `RUBYLIB` have the highest priority when loading
183    /// features, the returned paths should appear first in `$LOAD_PATH`.
184    ///
185    /// # Examples
186    ///
187    /// ```
188    /// use std::ffi::OsStr;
189    /// use std::path::Path;
190    /// use mezzaluna_load_path::Rubylib;
191    ///
192    /// # #[cfg(unix)]
193    /// # fn example() -> Option<()> {
194    /// let loader = Rubylib::with_rubylib(
195    ///     OsStr::new("/home/artichoke/src:/usr/share/artichoke:_lib"),
196    /// )?;
197    ///
198    /// let load_paths = loader.into_load_path();
199    /// assert_eq!(
200    ///     load_paths,
201    ///     [
202    ///         Path::new("/home/artichoke/src"),
203    ///         Path::new("/usr/share/artichoke"),
204    ///         Path::new("_lib"),
205    ///     ]
206    /// );
207    /// # Some(())
208    /// # }
209    /// # #[cfg(unix)]
210    /// # example().unwrap();
211    /// ```
212    #[inline]
213    #[must_use]
214    pub fn into_load_path(self) -> Vec<PathBuf> {
215        self.load_path.into()
216    }
217}
218
219#[cfg(all(test, unix))]
220mod tests {
221    use std::env;
222    use std::path::Path;
223
224    use super::*;
225
226    #[test]
227    fn with_rubylib_env_var() {
228        // SAFETY: This is the only test that manipulates the environment.
229        unsafe {
230            env::remove_var("RUBYLIB");
231        }
232        let loader = Rubylib::new();
233        assert!(loader.is_none());
234
235        // SAFETY: This is the only test that manipulates the environment.
236        unsafe {
237            env::set_var("RUBYLIB", "");
238        }
239        let loader = Rubylib::new();
240        assert!(loader.is_none());
241
242        // SAFETY: This is the only test that manipulates the environment.
243        unsafe {
244            env::set_var("RUBYLIB", "/home/artichoke/src:/usr/share/artichoke:_lib");
245        }
246        let loader = Rubylib::new().unwrap();
247
248        assert_eq!(loader.load_path().len(), 3);
249
250        let mut iter = loader.load_path().iter();
251        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
252        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
253        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
254        assert_eq!(iter.next(), None);
255
256        let load_path = loader.into_load_path();
257        assert_eq!(load_path.len(), 3);
258
259        let mut iter = load_path.iter();
260        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
261        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
262        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
263        assert_eq!(iter.next(), None);
264    }
265
266    #[test]
267    fn test_load_path_is_set_on_construction() {
268        let loader = Rubylib::with_rubylib("/home/artichoke/src:/usr/share/artichoke:_lib").unwrap();
269
270        assert_eq!(loader.load_path().len(), 3);
271
272        let mut iter = loader.load_path().iter();
273        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
274        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
275        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
276        assert_eq!(iter.next(), None);
277
278        let load_path = loader.into_load_path();
279        assert_eq!(load_path.len(), 3);
280
281        let mut iter = load_path.iter();
282        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
283        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
284        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
285        assert_eq!(iter.next(), None);
286    }
287
288    #[test]
289    fn test_empty_rubylib_is_none() {
290        let loader = Rubylib::with_rubylib("");
291        assert!(loader.is_none());
292    }
293
294    #[test]
295    fn test_empty_rubylib_paths_are_filtered() {
296        // ```console
297        // $ ruby -e 'puts $:'
298        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
299        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
300        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
301        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
302        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
303        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
304        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
305        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
306        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
307        // $ RUBYLIB= ruby -e 'puts $:'
308        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
309        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
310        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
311        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
312        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
313        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
314        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
315        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
316        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
317        // $ RUBYLIB=::: ruby -e 'puts $:'
318        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
319        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
320        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
321        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
322        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
323        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
324        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
325        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
326        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
327        // ```
328        let loader = Rubylib::with_rubylib(":::::::::::::::::");
329        assert!(loader.is_none());
330
331        let loader = Rubylib::with_rubylib(":::/home/artichoke/src:::/usr/share/artichoke:::_lib:::").unwrap();
332
333        assert_eq!(loader.load_path().len(), 3);
334
335        let mut iter = loader.load_path().iter();
336        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
337        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
338        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
339        assert_eq!(iter.next(), None);
340
341        let load_path = loader.into_load_path();
342        assert_eq!(load_path.len(), 3);
343
344        let mut iter = load_path.iter();
345        assert_eq!(iter.next().unwrap(), Path::new("/home/artichoke/src"));
346        assert_eq!(iter.next().unwrap(), Path::new("/usr/share/artichoke"));
347        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
348        assert_eq!(iter.next(), None);
349    }
350
351    #[test]
352    fn test_paths_taken_verbatim() {
353        // Relative paths are not resolved, duplicates are not removed, paths
354        // are not normalized:
355        //
356        // ```console
357        // $ RUBYLIB=.:.:`pwd`:`pwd`:/Users/:/Users ruby -e 'puts $:'
358        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
359        // .
360        // .
361        // /Users/lopopolo/dev/artichoke/artichoke
362        // /Users/lopopolo/dev/artichoke/artichoke
363        // /Users/
364        // /Users
365        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
366        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
367        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
368        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
369        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
370        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
371        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
372        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
373        // ```
374        let loader = Rubylib::with_rubylib(
375            ".:.:/Users/lopopolo/dev/artichoke/artichoke:/Users/lopopolo/dev/artichoke/artichoke:/Users/:/Users",
376        )
377        .unwrap();
378
379        assert_eq!(loader.load_path().len(), 6);
380
381        let mut iter = loader.load_path().iter();
382        assert_eq!(iter.next().unwrap(), Path::new("."));
383        assert_eq!(iter.next().unwrap(), Path::new("."));
384        assert_eq!(
385            iter.next().unwrap(),
386            Path::new("/Users/lopopolo/dev/artichoke/artichoke")
387        );
388        assert_eq!(
389            iter.next().unwrap(),
390            Path::new("/Users/lopopolo/dev/artichoke/artichoke")
391        );
392        assert_eq!(iter.next().unwrap(), Path::new("/Users/"));
393        assert_eq!(iter.next().unwrap(), Path::new("/Users"));
394        assert_eq!(iter.next(), None);
395
396        let load_path = loader.into_load_path();
397        assert_eq!(load_path.len(), 6);
398
399        let mut iter = load_path.iter();
400        assert_eq!(iter.next().unwrap(), Path::new("."));
401        assert_eq!(iter.next().unwrap(), Path::new("."));
402        assert_eq!(
403            iter.next().unwrap(),
404            Path::new("/Users/lopopolo/dev/artichoke/artichoke")
405        );
406        assert_eq!(
407            iter.next().unwrap(),
408            Path::new("/Users/lopopolo/dev/artichoke/artichoke")
409        );
410        assert_eq!(iter.next().unwrap(), Path::new("/Users/"));
411        assert_eq!(iter.next().unwrap(), Path::new("/Users"));
412        assert_eq!(iter.next(), None);
413    }
414}
415
416#[cfg(all(test, windows))]
417mod tests {
418    use std::env;
419    use std::path::Path;
420
421    use super::*;
422
423    #[test]
424    fn with_rubylib_env_var() {
425        // SAFETY: This is the only test that manipulates the environment.
426        unsafe {
427            env::remove_var("RUBYLIB");
428        }
429        let loader = Rubylib::new();
430        assert!(loader.is_none());
431
432        // SAFETY: This is the only test that manipulates the environment.
433        unsafe {
434            env::set_var("RUBYLIB", "");
435        }
436        let loader = Rubylib::new();
437        assert!(loader.is_none());
438
439        // SAFETY: This is the only test that manipulates the environment.
440        unsafe {
441            env::set_var("RUBYLIB", "c:/home/artichoke/src;c:/usr/share/artichoke;_lib");
442        }
443        let loader = Rubylib::new().unwrap();
444
445        assert_eq!(loader.load_path().len(), 3);
446
447        let mut iter = loader.load_path().iter();
448        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
449        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
450        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
451        assert_eq!(iter.next(), None);
452
453        let load_path = loader.into_load_path();
454        assert_eq!(load_path.len(), 3);
455
456        let mut iter = load_path.iter();
457        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
458        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
459        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
460        assert_eq!(iter.next(), None);
461    }
462
463    #[test]
464    fn test_load_path_is_set_on_construction() {
465        let loader = Rubylib::with_rubylib("c:/home/artichoke/src;c:/usr/share/artichoke;_lib").unwrap();
466
467        assert_eq!(loader.load_path().len(), 3);
468
469        let mut iter = loader.load_path().iter();
470        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
471        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
472        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
473        assert_eq!(iter.next(), None);
474
475        let load_path = loader.into_load_path();
476        assert_eq!(load_path.len(), 3);
477
478        let mut iter = load_path.iter();
479        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
480        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
481        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
482        assert_eq!(iter.next(), None);
483    }
484
485    #[test]
486    fn test_empty_rubylib_is_none() {
487        let loader = Rubylib::with_rubylib("");
488        assert!(loader.is_none());
489    }
490
491    #[test]
492    fn test_empty_rubylib_paths_are_filtered() {
493        // ```console
494        // $ ruby -e 'puts $:'
495        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
496        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
497        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
498        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
499        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
500        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
501        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
502        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
503        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
504        // $ RUBYLIB= ruby -e 'puts $:'
505        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
506        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
507        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
508        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
509        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
510        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
511        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
512        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
513        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
514        // $ RUBYLIB=::: ruby -e 'puts $:'
515        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
516        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
517        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
518        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
519        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
520        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
521        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
522        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
523        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
524        // ```
525        let loader = Rubylib::with_rubylib(";;;;;;;;;;;;;;;;");
526        assert!(loader.is_none());
527
528        let loader = Rubylib::with_rubylib(";;;c:/home/artichoke/src;;;c:/usr/share/artichoke;;;_lib;;;").unwrap();
529
530        assert_eq!(loader.load_path().len(), 3);
531
532        let mut iter = loader.load_path().iter();
533        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
534        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
535        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
536        assert_eq!(iter.next(), None);
537
538        let load_path = loader.into_load_path();
539        assert_eq!(load_path.len(), 3);
540
541        let mut iter = load_path.iter();
542        assert_eq!(iter.next().unwrap(), Path::new("c:/home/artichoke/src"));
543        assert_eq!(iter.next().unwrap(), Path::new("c:/usr/share/artichoke"));
544        assert_eq!(iter.next().unwrap(), Path::new("_lib"));
545        assert_eq!(iter.next(), None);
546    }
547
548    #[test]
549    fn test_paths_taken_verbatim() {
550        // Relative paths are not resolved, duplicates are not removed, paths
551        // are not normalized:
552        //
553        // ```console
554        // $ RUBYLIB=.:.:`pwd`:`pwd`:/Users/:/Users ruby -e 'puts $:'
555        // /usr/local/Cellar/rbenv/1.2.0/rbenv.d/exec/gem-rehash
556        // .
557        // .
558        // /Users/lopopolo/dev/artichoke/artichoke
559        // /Users/lopopolo/dev/artichoke/artichoke
560        // /Users/
561        // /Users
562        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0
563        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby/3.2.0/x86_64-darwin22
564        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/site_ruby
565        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0
566        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby/3.2.0/x86_64-darwin22
567        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/vendor_ruby
568        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0
569        // /usr/local/var/rbenv/versions/3.2.2/lib/ruby/3.2.0/x86_64-darwin22
570        // ```
571        let loader = Rubylib::with_rubylib(
572            ".;.;c:/lopopolo/dev/artichoke/artichoke;c:/lopopolo/dev/artichoke/artichoke;c:/var/;c:/var",
573        )
574        .unwrap();
575
576        assert_eq!(loader.load_path().len(), 6);
577
578        let mut iter = loader.load_path().iter();
579        assert_eq!(iter.next().unwrap(), Path::new("."));
580        assert_eq!(iter.next().unwrap(), Path::new("."));
581        assert_eq!(iter.next().unwrap(), Path::new("c:/lopopolo/dev/artichoke/artichoke"));
582        assert_eq!(iter.next().unwrap(), Path::new("c:/lopopolo/dev/artichoke/artichoke"));
583        assert_eq!(iter.next().unwrap(), Path::new("c:/var/"));
584        assert_eq!(iter.next().unwrap(), Path::new("c:/var"));
585        assert_eq!(iter.next(), None);
586
587        let load_path = loader.into_load_path();
588        assert_eq!(load_path.len(), 6);
589
590        let mut iter = load_path.iter();
591        assert_eq!(iter.next().unwrap(), Path::new("."));
592        assert_eq!(iter.next().unwrap(), Path::new("."));
593        assert_eq!(iter.next().unwrap(), Path::new("c:/lopopolo/dev/artichoke/artichoke"));
594        assert_eq!(iter.next().unwrap(), Path::new("c:/lopopolo/dev/artichoke/artichoke"));
595        assert_eq!(iter.next().unwrap(), Path::new("c:/var/"));
596        assert_eq!(iter.next().unwrap(), Path::new("c:/var"));
597        assert_eq!(iter.next(), None);
598    }
599}