lib.rs
  1  #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
  2  #![doc = include_str!("../README.md")]
  3  // @@ begin lint list maintained by maint/add_warning @@
  4  #![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
  5  #![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
  6  #![warn(missing_docs)]
  7  #![warn(noop_method_call)]
  8  #![warn(unreachable_pub)]
  9  #![warn(clippy::all)]
 10  #![deny(clippy::await_holding_lock)]
 11  #![deny(clippy::cargo_common_metadata)]
 12  #![deny(clippy::cast_lossless)]
 13  #![deny(clippy::checked_conversions)]
 14  #![warn(clippy::cognitive_complexity)]
 15  #![deny(clippy::debug_assert_with_mut_call)]
 16  #![deny(clippy::exhaustive_enums)]
 17  #![deny(clippy::exhaustive_structs)]
 18  #![deny(clippy::expl_impl_clone_on_copy)]
 19  #![deny(clippy::fallible_impl_from)]
 20  #![deny(clippy::implicit_clone)]
 21  #![deny(clippy::large_stack_arrays)]
 22  #![warn(clippy::manual_ok_or)]
 23  #![deny(clippy::missing_docs_in_private_items)]
 24  #![warn(clippy::needless_borrow)]
 25  #![warn(clippy::needless_pass_by_value)]
 26  #![warn(clippy::option_option)]
 27  #![deny(clippy::print_stderr)]
 28  #![deny(clippy::print_stdout)]
 29  #![warn(clippy::rc_buffer)]
 30  #![deny(clippy::ref_option_ref)]
 31  #![warn(clippy::semicolon_if_nothing_returned)]
 32  #![warn(clippy::trait_duplication_in_bounds)]
 33  #![deny(clippy::unchecked_duration_subtraction)]
 34  #![deny(clippy::unnecessary_wraps)]
 35  #![warn(clippy::unseparated_literal_suffix)]
 36  #![deny(clippy::unwrap_used)]
 37  #![deny(clippy::mod_module_files)]
 38  #![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
 39  #![allow(clippy::uninlined_format_args)]
 40  #![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
 41  #![allow(clippy::result_large_err)] // temporary workaround for arti#587
 42  #![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
 43  #![allow(clippy::needless_lifetimes)] // See arti#1765
 44  //! <!-- @@ end lint list maintained by maint/add_warning @@ -->
 45  
 46  use std::collections::BinaryHeap;
 47  use std::fmt;
 48  use std::mem;
 49  use std::ops::{RangeInclusive, RangeToInclusive};
 50  use std::path::Path;
 51  use std::time::Duration;
 52  
 53  pub mod iter;
 54  pub mod n_key_list;
 55  pub mod n_key_set;
 56  pub mod rangebounds;
 57  pub mod retry;
 58  pub mod test_rng;
 59  
 60  mod byte_qty;
 61  pub use byte_qty::ByteQty;
 62  
 63  pub use paste::paste;
 64  
 65  use rand::Rng;
 66  
 67  /// Sealed
 68  mod sealed {
 69      /// Sealed
 70      pub trait Sealed {}
 71  }
 72  use sealed::Sealed;
 73  
 74  // ----------------------------------------------------------------------
 75  
 76  /// Function with the signature of `Debug::fmt` that just prints `".."`
 77  ///
 78  /// ```
 79  /// use educe::Educe;
 80  /// use tor_basic_utils::skip_fmt;
 81  ///
 82  /// #[derive(Educe, Default)]
 83  /// #[educe(Debug)]
 84  /// struct Wombat {
 85  ///     visible: usize,
 86  ///
 87  ///     #[educe(Debug(method = "skip_fmt"))]
 88  ///     invisible: [u8; 2],
 89  /// }
 90  ///
 91  /// assert_eq!( format!("{:?}", &Wombat::default()),
 92  ///             "Wombat { visible: 0, invisible: .. }" );
 93  /// ```
 94  pub fn skip_fmt<T>(_: &T, f: &mut fmt::Formatter) -> fmt::Result {
 95      /// Inner function avoids code bloat due to generics
 96      fn inner(f: &mut fmt::Formatter) -> fmt::Result {
 97          write!(f, "..")
 98      }
 99      inner(f)
100  }
101  
102  // ----------------------------------------------------------------------
103  
104  /// Extension trait to provide `.strip_suffix_ignore_ascii_case()` etc.
105  // Using `.as_ref()` as a supertrait lets us make the method a provided one.
106  pub trait StrExt: AsRef<str> {
107      /// Like `str.strip_suffix()` but ASCII-case-insensitive
108      fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
109          let whole = self.as_ref();
110          let suffix_start = whole.len().checked_sub(suffix.len())?;
111          whole[suffix_start..]
112              .eq_ignore_ascii_case(suffix)
113              .then(|| &whole[..suffix_start])
114      }
115  
116      /// Like `str.ends_with()` but ASCII-case-insensitive
117      fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool {
118          self.strip_suffix_ignore_ascii_case(suffix).is_some()
119      }
120  }
121  impl StrExt for str {}
122  
123  // ----------------------------------------------------------------------
124  
125  /// Extension trait to provide `.gen_range_checked()`
126  pub trait RngExt: Rng {
127      /// Generate a random value in the given range.
128      ///
129      /// This function is optimised for the case that only a single sample is made from the given range. See also the [`Uniform`](rand::distributions::uniform::Uniform)  distribution type which may be faster if sampling from the same range repeatedly.
130      ///
131      /// If the supplied range is empty, returns `None`.
132      ///
133      /// (This is a non-panicking version of [`Rng::gen_range`].)
134      ///
135      /// ### Example
136      ///
137      /// ```
138      /// use rand::thread_rng;
139      /// use tor_basic_utils::RngExt as _;
140      //
141      // Fake plastic imitation tor_error, since that's actually higher up the stack
142      /// # #[macro_use]
143      /// # mod tor_error {
144      /// #     #[derive(Debug)]
145      /// #     pub struct Bug;
146      /// #     pub fn internal() {} // makes `use` work
147      /// # }
148      /// # macro_rules! internal { { $x:expr } => { Bug } }
149      //
150      /// use tor_error::{Bug, internal};
151      ///
152      /// fn choose(slice: &[i32]) -> Result<i32, Bug> {
153      ///     let index = thread_rng()
154      ///         .gen_range_checked(0..slice.len())
155      ///         .ok_or_else(|| internal!("empty slice"))?;
156      ///     Ok(slice[index])
157      /// }
158      ///
159      /// assert_eq!(choose(&[42]).unwrap(), 42);
160      /// let _: Bug = choose(&[]).unwrap_err();
161      /// ```
162      fn gen_range_checked<T, R>(&mut self, range: R) -> Option<T>
163      where
164          T: rand::distributions::uniform::SampleUniform,
165          R: rand::distributions::uniform::SampleRange<T>,
166      {
167          if range.is_empty() {
168              None
169          } else {
170              #[allow(clippy::disallowed_methods)]
171              Some(Rng::gen_range(self, range))
172          }
173      }
174  
175      /// Generate a random value in the given upper-bounded-only range.
176      ///
177      /// For use with an inclusive upper-bounded-only range,
178      /// with types that implement `GenRangeInfallible`
179      /// (that necessarily then implement the appropriate `rand` traits).
180      ///
181      /// This function is optimised for the case that only a single sample is made from the given range. See also the [`Uniform`](rand::distributions::uniform::Uniform)  distribution type which may be faster if sampling from the same range repeatedly.
182      ///
183      /// ### Example
184      ///
185      /// ```
186      /// use std::time::Duration;
187      /// use rand::thread_rng;
188      /// use tor_basic_utils::RngExt as _;
189      ///
190      /// fn stochastic_sleep(max: Duration) {
191      ///     let chosen_delay = thread_rng()
192      ///         .gen_range_infallible(..=max);
193      ///     std::thread::sleep(chosen_delay);
194      /// }
195      /// ```
196      fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
197      where
198          T: GenRangeInfallible,
199      {
200          self.gen_range_checked(T::lower_bound()..=range.end)
201              .expect("GenRangeInfallible type with an empty lower_bound()..=T range")
202      }
203  }
204  impl<T: Rng> RngExt for T {}
205  
206  /// Types that can be infallibly sampled using `gen_range_infallible`
207  ///
208  /// In addition to the supertraits, the implementor of this trait must guarantee that:
209  ///
210  /// `<Self as GenRangeInfallible>::lower_bound() ..= UPPER`
211  /// is a nonempty range for every value of `UPPER`.
212  //
213  // One might think that this trait is wrong because we might want to be able to
214  // implement gen_range_infallible for arguments other than RangeToInclusive<T>.
215  // However, double-ended ranges are inherently fallible because the actual values
216  // might be in the wrong order.  Non-inclusive ranges are fallible because the
217  // upper bound might be zero, unless a NonZero type is used, which seems like a further
218  // complication that we probably don't want to introduce here.  That leaves lower-bounded
219  // ranges, but those are very rare.
220  pub trait GenRangeInfallible: rand::distributions::uniform::SampleUniform + Ord
221  where
222      RangeInclusive<Self>: rand::distributions::uniform::SampleRange<Self>,
223  {
224      /// The usual lower bound, for converting a `RangeToInclusive` to a `RangeInclusive`
225      ///
226      /// Only makes sense with types with a sensible lower bound, such as zero.
227      fn lower_bound() -> Self;
228  }
229  
230  impl GenRangeInfallible for Duration {
231      fn lower_bound() -> Self {
232          Duration::ZERO
233      }
234  }
235  
236  // ----------------------------------------------------------------------
237  
238  /// Implementation of `ErrorKind::NotADirectory` that doesn't require Nightly
239  pub trait IoErrorExt: Sealed {
240      /// Is this `io::ErrorKind::NotADirectory` ?
241      fn is_not_a_directory(&self) -> bool;
242  }
243  impl Sealed for std::io::Error {}
244  impl IoErrorExt for std::io::Error {
245      fn is_not_a_directory(&self) -> bool {
246          self.raw_os_error()
247              == Some(
248                  #[cfg(target_family = "unix")]
249                  libc::ENOTDIR,
250                  #[cfg(target_family = "windows")]
251                  {
252                      /// Obtained from Rust stdlib source code
253                      /// See also:
254                      ///   <https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499->
255                      /// (although the documentation is anaemic) and
256                      /// <https://github.com/rust-lang/rust/pull/79965>
257                      const ERROR_DIRECTORY: i32 = 267;
258                      ERROR_DIRECTORY
259                  },
260              )
261      }
262  }
263  
264  // ----------------------------------------------------------------------
265  
266  /// Implementation of `BinaryHeap::retain` that doesn't require Nightly
267  pub trait BinaryHeapExt<T> {
268      /// Remove all elements for which `f` returns `false`
269      ///
270      /// Performance is not great right now - the algorithm is `O(n*log(n))`
271      /// where `n` is the number of elements in the heap (not the number removed).
272      ///
273      /// The name is `retain_ext` to avoid a name collision with the unstable function,
274      /// which would require the use of UFCS and make this unergonomic.
275      fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F);
276  }
277  impl<T: Ord> BinaryHeapExt<T> for BinaryHeap<T> {
278      fn retain_ext<F: FnMut(&T) -> bool>(&mut self, f: F) {
279          let items = mem::take(self).into_iter();
280          *self = items.filter(f).collect();
281      }
282  }
283  
284  // ----------------------------------------------------------------------
285  
286  /// Renaming of `Path::display` as `display_lossy`
287  pub trait PathExt: Sealed {
288      /// Display this `Path` as an approximate string, for human consumption in messages
289      ///
290      /// Operating system paths cannot always be faithfully represented as Rust strings,
291      /// because they might not be valid Unicode.
292      ///
293      /// This helper method provides a way to display a string for human users.
294      /// **This may lose information** so should only be used for error messages etc.
295      ///
296      /// This method is exactly the same as [`std::path::Path::display`],
297      /// but with a different and more discouraging name.
298      fn display_lossy(&self) -> std::path::Display<'_>;
299  }
300  impl Sealed for Path {}
301  impl PathExt for Path {
302      #[allow(clippy::disallowed_methods)]
303      fn display_lossy(&self) -> std::path::Display<'_> {
304          self.display()
305      }
306  }
307  
308  // ----------------------------------------------------------------------
309  
310  /// Define an "accessor trait", which describes structs that have fields of certain types
311  ///
312  /// This can be useful if a large struct, living high up in the dependency graph,
313  /// contains fields that lower-lever crates want to be able to use without having
314  /// to copy the data about etc.
315  ///
316  /// ```
317  /// // imagine this in the lower-level module
318  /// pub trait Supertrait {}
319  /// use tor_basic_utils::define_accessor_trait;
320  /// define_accessor_trait! {
321  ///     pub trait View: Supertrait {
322  ///         lorem: String,
323  ///         ipsum: usize,
324  ///         +
325  ///         fn other_accessor(&self) -> bool;
326  ///         // any other trait items can go here
327  ///    }
328  /// }
329  ///
330  /// fn test_view<V: View>(v: &V) {
331  ///     assert_eq!(v.lorem(), "sit");
332  ///     assert_eq!(v.ipsum(), &42);
333  /// }
334  ///
335  /// // imagine this in the higher-level module
336  /// use derive_more::AsRef;
337  /// #[derive(AsRef)]
338  /// struct Everything {
339  ///     #[as_ref] lorem: String,
340  ///     #[as_ref] ipsum: usize,
341  ///     dolor: Vec<()>,
342  /// }
343  /// impl Supertrait for Everything { }
344  /// impl View for Everything {
345  ///     fn other_accessor(&self) -> bool { false }
346  /// }
347  ///
348  /// let everything = Everything {
349  ///     lorem: "sit".into(),
350  ///     ipsum: 42,
351  ///     dolor: vec![()],
352  /// };
353  ///
354  /// test_view(&everything);
355  /// ```
356  ///
357  /// ### Generated code
358  ///
359  /// ```
360  /// # pub trait Supertrait { }
361  /// pub trait View: AsRef<String> + AsRef<usize> + Supertrait {
362  ///     fn lorem(&self) -> &String { self.as_ref() }
363  ///     fn ipsum(&self) -> &usize { self.as_ref() }
364  /// }
365  /// ```
366  #[macro_export]
367  macro_rules! define_accessor_trait {
368      {
369          $( #[ $attr:meta ])*
370          $vis:vis trait $Trait:ident $( : $( $Super:path )* )? {
371              $( $accessor:ident: $type:ty, )*
372              $( + $( $rest:tt )* )?
373          }
374      } => {
375          $( #[ $attr ])*
376          $vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )?
377          {
378              $(
379                  /// Access the field
380                  fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) }
381              )*
382              $(
383                  $( $rest )*
384              )?
385          }
386      }
387  }
388  
389  // ----------------------------------------------------------------------
390  
391  /// Helper for assisting with macro "argument" defaulting
392  ///
393  /// ```ignore
394  /// macro_coalesce_args!{ [ something ]  ... }  // =>   something
395  /// macro_coalesce_args!{ [ ], [ other ] ... }  // =>   other
396  /// // etc.
397  /// ```
398  ///
399  /// ### Usage note
400  ///
401  /// It is generally possible to avoid use of `macro_coalesce_args`, at the cost of
402  /// providing many alternative matcher patterns.  Using `macro_coalesce_args` can make
403  /// it possible to provide a single pattern with the optional items in `$( )?`.
404  ///
405  /// This is valuable because a single pattern with some optional items
406  /// makes much better documentation than several patterns which the reader must compare
407  /// by eye - and it also simplifies the implementation.
408  ///
409  /// `macro_coalesce_args` takes each of its possible expansions in `[ ]` and returns
410  /// the first nonempty one.
411  #[macro_export]
412  macro_rules! macro_first_nonempty {
413      { [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* };
414      { [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => {
415          $crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* }
416      };
417  }
418  
419  // ----------------------------------------------------------------------
420  
421  /// Define `Debug` to print as hex
422  ///
423  /// # Usage
424  ///
425  /// ```ignore
426  /// impl_debug_hex! { $type }
427  /// impl_debug_hex! { $type . $field_accessor }
428  /// impl_debug_hex! { $type , $accessor_fn }
429  /// ```
430  ///
431  /// By default, this expects `$type` to implement `AsRef<[u8]>`.
432  ///
433  /// Or, you can supply a series of tokens `$field_accessor`,
434  /// which will be used like this: `self.$field_accessor.as_ref()`
435  /// to get a `&[u8]`.
436  ///
437  /// Or, you can supply `$accessor: fn(&$type) -> &[u8]`.
438  ///
439  /// # Examples
440  ///
441  /// ```
442  /// use tor_basic_utils::impl_debug_hex;
443  /// #[derive(Default)]
444  /// struct FourBytes([u8; 4]);
445  /// impl AsRef<[u8]> for FourBytes { fn as_ref(&self) -> &[u8] { &self.0 } }
446  /// impl_debug_hex! { FourBytes }
447  ///
448  /// assert_eq!(
449  ///     format!("{:?}", FourBytes::default()),
450  ///     "FourBytes(00000000)",
451  /// );
452  /// ```
453  ///
454  /// ```
455  /// use tor_basic_utils::impl_debug_hex;
456  /// #[derive(Default)]
457  /// struct FourBytes([u8; 4]);
458  /// impl_debug_hex! { FourBytes .0 }
459  ///
460  /// assert_eq!(
461  ///     format!("{:?}", FourBytes::default()),
462  ///     "FourBytes(00000000)",
463  /// );
464  /// ```
465  ///
466  /// ```
467  /// use tor_basic_utils::impl_debug_hex;
468  /// struct FourBytes([u8; 4]);
469  /// impl_debug_hex! { FourBytes, |self_| &self_.0 }
470  ///
471  /// assert_eq!(
472  ///     format!("{:?}", FourBytes([1,2,3,4])),
473  ///     "FourBytes(01020304)",
474  /// )
475  /// ```
476  #[macro_export]
477  macro_rules! impl_debug_hex {
478      { $type:ty $(,)? } => {
479          $crate::impl_debug_hex! { $type, |self_| <$type as AsRef<[u8]>>::as_ref(&self_) }
480      };
481      { $type:ident . $($accessor:tt)+ } => {
482          $crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() }
483      };
484      { $type:ty, $obtain:expr $(,)? } => {
485          impl std::fmt::Debug for $type {
486              fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
487                  use std::fmt::Write;
488                  let obtain: fn(&$type) -> &[u8] = $obtain;
489                  let bytes: &[u8] = obtain(self);
490                  write!(f, "{}(", stringify!($type))?;
491                  for b in bytes {
492                      write!(f, "{:02x}", b)?;
493                  }
494                  write!(f, ")")?;
495                  Ok(())
496              }
497          }
498      };
499  }
500  
501  // ----------------------------------------------------------------------
502  
503  /// Helper for defining a struct which can be (de)serialized several ways, including "natively"
504  ///
505  /// Ideally we would have
506  /// ```rust ignore
507  /// #[derive(Deserialize)]
508  /// #[serde(try_from=Possibilities)]
509  /// struct Main { /* principal definition */ }
510  ///
511  /// #[derive(Deserialize)]
512  /// #[serde(untagged)]
513  /// enum Possibilities { Main(Main), Other(OtherRepr) }
514  ///
515  /// #[derive(Deserialize)]
516  /// struct OtherRepr { /* other representation we still want to read */ }
517  ///
518  /// impl TryFrom<Possibilities> for Main { /* ... */ }
519  /// ```
520  ///
521  /// But the impl for `Possibilities` ends up honouring the `try_from` on `Main`
522  /// so is recursive.
523  ///
524  /// We solve that (ab)using serde's remote feature,
525  /// on a second copy of the struct definition.
526  ///
527  /// See the Example for instructions.
528  /// It is important to **add test cases**
529  /// for all the representations you expect to parse and serialise,
530  /// since there are easy-to-write bugs,
531  /// for example omitting some of the necessary attributes.
532  ///
533  /// # Generated output:
534  ///
535  ///  * The original struct definition, unmodified
536  ///  * `#[derive(Serialize, Deserialize)] struct $main_Raw { }`
537  ///
538  /// The `$main_Raw` struct ought not normally be to constructed anywhere,
539  /// and *isn't* convertible to or from the near-identical `$main` struct.
540  /// It exists only as a thing to feed to the serde remove derive,
541  /// and name in `with=`.
542  ///
543  /// # Example
544  ///
545  /// ```
546  /// use serde::{Deserialize, Serialize};
547  /// use tor_basic_utils::derive_serde_raw;
548  ///
549  /// derive_serde_raw! {
550  ///     #[derive(Deserialize, Serialize, Default, Clone, Debug)]
551  ///     #[serde(try_from="BridgeConfigBuilderSerde", into="BridgeConfigBuilderSerde")]
552  ///     pub struct BridgeConfigBuilder = "BridgeConfigBuilder" {
553  ///         transport: Option<String>,
554  ///         //...
555  ///     }
556  /// }
557  ///
558  /// #[derive(Serialize,Deserialize)]
559  /// #[serde(untagged)]
560  /// enum BridgeConfigBuilderSerde {
561  ///     BridgeLine(String),
562  ///     Dict(#[serde(with="BridgeConfigBuilder_Raw")] BridgeConfigBuilder),
563  /// }
564  ///
565  /// impl TryFrom<BridgeConfigBuilderSerde> for BridgeConfigBuilder { //...
566  /// #    type Error = std::io::Error;
567  /// #    fn try_from(_: BridgeConfigBuilderSerde) -> Result<Self, Self::Error> { todo!() } }
568  /// impl From<BridgeConfigBuilder> for BridgeConfigBuilderSerde { //...
569  /// #    fn from(_: BridgeConfigBuilder) -> BridgeConfigBuilderSerde { todo!() } }
570  /// ```
571  #[macro_export]
572  macro_rules! derive_serde_raw { {
573      $( #[ $($attrs:meta)* ] )*
574      $vis:vis struct $main:ident=$main_s:literal
575      $($body:tt)*
576  } => {
577      $(#[ $($attrs)* ])*
578      $vis struct $main
579      $($body)*
580  
581      $crate::paste! {
582          #[allow(non_camel_case_types)]
583          #[derive(Serialize, Deserialize)]
584          #[serde(remote=$main_s)]
585          struct [< $main _Raw >]
586          $($body)*
587      }
588  } }
589  
590  // ----------------------------------------------------------------------
591  
592  #[cfg(test)]
593  mod test {
594      // @@ begin test lint list maintained by maint/add_warning @@
595      #![allow(clippy::bool_assert_comparison)]
596      #![allow(clippy::clone_on_copy)]
597      #![allow(clippy::dbg_macro)]
598      #![allow(clippy::mixed_attributes_style)]
599      #![allow(clippy::print_stderr)]
600      #![allow(clippy::print_stdout)]
601      #![allow(clippy::single_char_pattern)]
602      #![allow(clippy::unwrap_used)]
603      #![allow(clippy::unchecked_duration_subtraction)]
604      #![allow(clippy::useless_vec)]
605      #![allow(clippy::needless_pass_by_value)]
606      //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
607      use super::*;
608  
609      #[test]
610      fn test_strip_suffix_ignore_ascii_case() {
611          assert_eq!(
612              "hi there".strip_suffix_ignore_ascii_case("THERE"),
613              Some("hi ")
614          );
615          assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None);
616          assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some(""));
617          assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None);
618      }
619  }