This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch avro-3839-replace-lazy-static-with-oncelock
in repository https://gitbox.apache.org/repos/asf/avro.git

commit 674ff2b0a453b874fa407b61e01803bd336ad622
Author: Martin Tzvetanov Grigorov <[email protected]>
AuthorDate: Mon Aug 21 15:02:35 2023 +0300

    AVRO-3839: [Rust] Replace lazy_static crate with std::sync::OnceLock
    
    Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---
 lang/rust/Cargo.lock                     |   2 -
 lang/rust/avro/Cargo.toml                |   1 -
 lang/rust/avro/src/rabin.rs              |  13 +-
 lang/rust/avro/src/schema.rs             |  44 +++--
 lang/rust/avro/tests/io.rs               | 271 +++++++++++++++++++++----------
 lang/rust/avro/tests/schema.rs           |  68 ++++----
 lang/rust/avro_test_helper/Cargo.toml    |   1 -
 lang/rust/avro_test_helper/src/logger.rs |  11 +-
 8 files changed, 266 insertions(+), 145 deletions(-)

diff --git a/lang/rust/Cargo.lock b/lang/rust/Cargo.lock
index bf7fc242a..4dcb29e2d 100644
--- a/lang/rust/Cargo.lock
+++ b/lang/rust/Cargo.lock
@@ -73,7 +73,6 @@ dependencies = [
  "criterion",
  "digest",
  "hex-literal",
- "lazy_static",
  "libflate",
  "log",
  "md-5",
@@ -117,7 +116,6 @@ dependencies = [
  "color-backtrace",
  "ctor",
  "env_logger",
- "lazy_static",
  "log",
  "ref_thread_local",
 ]
diff --git a/lang/rust/avro/Cargo.toml b/lang/rust/avro/Cargo.toml
index 29ef270d8..ed5721165 100644
--- a/lang/rust/avro/Cargo.toml
+++ b/lang/rust/avro/Cargo.toml
@@ -58,7 +58,6 @@ apache-avro-derive = { default-features = false, version = 
"0.16.0", path = "../
 bzip2 = { default-features = false, version = "0.4.4", optional = true }
 crc32fast = { default-features = false, version = "1.3.2", optional = true }
 digest = { default-features = false, version = "0.10.7", features = 
["core-api"] }
-lazy_static = { default-features = false, version = "1.4.0" }
 libflate = { default-features = false, version = "2.0.0", features = ["std"] }
 log = { default-features = false, version = "0.4.20" }
 num-bigint = { default-features = false, version = "0.4.3" }
diff --git a/lang/rust/avro/src/rabin.rs b/lang/rust/avro/src/rabin.rs
index fc63f8999..f860769eb 100644
--- a/lang/rust/avro/src/rabin.rs
+++ b/lang/rust/avro/src/rabin.rs
@@ -20,22 +20,23 @@ use digest::{
     consts::U8, core_api::OutputSizeUser, generic_array::GenericArray, 
FixedOutput,
     FixedOutputReset, HashMarker, Output, Reset, Update,
 };
-use lazy_static::lazy_static;
+use std::sync::OnceLock;
 
 const EMPTY: i64 = -4513414715797952619;
 
-lazy_static! {
-    static ref FPTABLE: [i64; 256] = {
+fn fp_table() -> &'static [i64; 256] {
+    static FPTABLE_ONCE: OnceLock<[i64; 256]> = OnceLock::new();
+    FPTABLE_ONCE.get_or_init(|| {
         let mut fp_table: [i64; 256] = [0; 256];
         for i in 0..256 {
             let mut fp = i;
             for _ in 0..8 {
                 fp = (fp as u64 >> 1) as i64 ^ (EMPTY & -(fp & 1));
             }
-            fp_table[i as usize] = fp
+            fp_table[i as usize] = fp;
         }
         fp_table
-    };
+    })
 }
 
 /// Implementation of the Rabin fingerprint algorithm using the Digest trait 
as described in 
[schema_fingerprints](https://avro.apache.org/docs/current/spec.html#schema_fingerprints).
@@ -94,7 +95,7 @@ impl Update for Rabin {
     fn update(&mut self, data: &[u8]) {
         for b in data {
             self.result = (self.result as u64 >> 8) as i64
-                ^ FPTABLE[((self.result ^ *b as i64) & 0xff) as usize];
+                ^ fp_table()[((self.result ^ *b as i64) & 0xff) as usize];
         }
     }
 }
diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index cd2e56bbf..ef82f9b96 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -18,7 +18,6 @@
 //! Logic for parsing and interacting with schemas in Avro format.
 use crate::{error::Error, types, util::MapHelper, AvroResult};
 use digest::Digest;
-use lazy_static::lazy_static;
 use regex::Regex;
 use serde::{
     ser::{SerializeMap, SerializeSeq},
@@ -34,19 +33,36 @@ use std::{
     hash::Hash,
     io::Read,
     str::FromStr,
+    sync::OnceLock,
 };
 use strum_macros::{EnumDiscriminants, EnumString};
 
-lazy_static! {
-    static ref ENUM_SYMBOL_NAME_R: Regex = 
Regex::new(r"^[A-Za-z_][A-Za-z0-9_]*$").unwrap();
+fn enum_symbol_name_r() -> &'static Regex {
+    static ENUM_SYMBOL_NAME_ONCE: OnceLock<Regex> = OnceLock::new();
+    ENUM_SYMBOL_NAME_ONCE.get_or_init(|| 
Regex::new(r"^[A-Za-z_][A-Za-z0-9_]*$").unwrap())
+}
 
-    // An optional namespace (with optional dots) followed by a name without 
any dots in it.
-    static ref SCHEMA_NAME_R: Regex =
-        
Regex::new(r"^((?P<namespace>([A-Za-z_][A-Za-z0-9_\.]*)*)\.)?(?P<name>[A-Za-z_][A-Za-z0-9_]*)$").unwrap();
+// An optional namespace (with optional dots) followed by a name without any 
dots in it.
+fn schema_name_r() -> &'static Regex {
+    static SCHEMA_NAME_ONCE: OnceLock<Regex> = OnceLock::new();
+    SCHEMA_NAME_ONCE.get_or_init(|| {
+        Regex::new(
+            
r"^((?P<namespace>([A-Za-z_][A-Za-z0-9_\.]*)*)\.)?(?P<name>[A-Za-z_][A-Za-z0-9_]*)$",
+        )
+        .unwrap()
+    })
+}
 
-    static ref FIELD_NAME_R: Regex = 
Regex::new(r"^[A-Za-z_][A-Za-z0-9_]*$").unwrap();
+fn field_name_r() -> &'static Regex {
+    static FIELD_NAME_ONCE: OnceLock<Regex> = OnceLock::new();
+    FIELD_NAME_ONCE.get_or_init(|| 
Regex::new(r"^[A-Za-z_][A-Za-z0-9_]*$").unwrap())
+}
 
-    static ref NAMESPACE_R: Regex = 
Regex::new(r"^([A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*)?$").unwrap();
+fn namespace_r() -> &'static Regex {
+    static NAMESPACE_ONCE: OnceLock<Regex> = OnceLock::new();
+    NAMESPACE_ONCE.get_or_init(|| {
+        
Regex::new(r"^([A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*)?$").unwrap()
+    })
 }
 
 /// Represents an Avro schema fingerprint
@@ -236,9 +252,9 @@ impl Name {
     }
 
     fn get_name_and_namespace(name: &str) -> AvroResult<(String, Namespace)> {
-        let caps = SCHEMA_NAME_R
+        let caps = schema_name_r()
             .captures(name)
-            .ok_or_else(|| Error::InvalidSchemaName(name.to_string(), 
SCHEMA_NAME_R.as_str()))?;
+            .ok_or_else(|| Error::InvalidSchemaName(name.to_string(), 
schema_name_r().as_str()))?;
         Ok((
             caps["name"].to_string(),
             caps.name("namespace").map(|s| s.as_str().to_string()),
@@ -269,10 +285,10 @@ impl Name {
             .filter(|ns| !ns.is_empty());
 
         if let Some(ref ns) = namespace {
-            if !NAMESPACE_R.is_match(ns) {
+            if !namespace_r().is_match(ns) {
                 return Err(Error::InvalidNamespace(
                     ns.to_string(),
-                    NAMESPACE_R.as_str(),
+                    namespace_r().as_str(),
                 ));
             }
         }
@@ -641,7 +657,7 @@ impl RecordField {
     ) -> AvroResult<Self> {
         let name = field.name().ok_or(Error::GetNameFieldFromRecord)?;
 
-        if !FIELD_NAME_R.is_match(&name) {
+        if !field_name_r().is_match(&name) {
             return Err(Error::FieldName(name));
         }
 
@@ -1532,7 +1548,7 @@ impl Parser {
         let mut existing_symbols: HashSet<&String> = 
HashSet::with_capacity(symbols.len());
         for symbol in symbols.iter() {
             // Ensure enum symbol names match [A-Za-z_][A-Za-z0-9_]*
-            if !ENUM_SYMBOL_NAME_R.is_match(symbol) {
+            if !enum_symbol_name_r().is_match(symbol) {
                 return Err(Error::EnumSymbolName(symbol.to_string()));
             }
 
diff --git a/lang/rust/avro/tests/io.rs b/lang/rust/avro/tests/io.rs
index b835fe7af..2b3aa2376 100644
--- a/lang/rust/avro/tests/io.rs
+++ b/lang/rust/avro/tests/io.rs
@@ -18,90 +18,189 @@
 //! Port of 
https://github.com/apache/avro/blob/release-1.9.1/lang/py/test/test_io.py
 use apache_avro::{from_avro_datum, to_avro_datum, types::Value, Error, Schema};
 use apache_avro_test_helper::TestResult;
-use lazy_static::lazy_static;
 use pretty_assertions::assert_eq;
-use std::io::Cursor;
+use std::{io::Cursor, sync::OnceLock};
 
-lazy_static! {
-    static ref SCHEMAS_TO_VALIDATE: Vec<(&'static str, Value)> = vec![
-        (r#""null""#, Value::Null),
-        (r#""boolean""#, Value::Boolean(true)),
-        (r#""string""#, Value::String("adsfasdf09809dsf-=adsf".to_string())),
-        (r#""bytes""#, Value::Bytes("12345abcd".to_string().into_bytes())),
-        (r#""int""#, Value::Int(1234)),
-        (r#""long""#, Value::Long(1234)),
-        (r#""float""#, Value::Float(1234.0)),
-        (r#""double""#, Value::Double(1234.0)),
-        (r#"{"type": "fixed", "name": "Test", "size": 1}"#, Value::Fixed(1, 
vec![b'B'])),
-        (r#"{"type": "enum", "name": "Test", "symbols": ["A", "B"]}"#, 
Value::Enum(1, "B".to_string())),
-        (r#"{"type": "array", "items": "long"}"#, 
Value::Array(vec![Value::Long(1), Value::Long(3), Value::Long(2)])),
-        (r#"{"type": "map", "values": "long"}"#, Value::Map([("a".to_string(), 
Value::Long(1i64)), ("b".to_string(), Value::Long(3i64)), ("c".to_string(), 
Value::Long(2i64))].iter().cloned().collect())),
-        (r#"["string", "null", "long"]"#, Value::Union(1, 
Box::new(Value::Null))),
-        (r#"{"type": "record", "name": "Test", "fields": [{"name": "f", 
"type": "long"}]}"#, Value::Record(vec![("f".to_string(), Value::Long(1))]))
-    ];
-
-    static ref BINARY_ENCODINGS: Vec<(i64, Vec<u8>)> = vec![
-        (0, vec![0x00]),
-        (-1, vec![0x01]),
-        (1, vec![0x02]),
-        (-2, vec![0x03]),
-        (2, vec![0x04]),
-        (-64, vec![0x7f]),
-        (64, vec![0x80, 0x01]),
-        (8192, vec![0x80, 0x80, 0x01]),
-        (-8193, vec![0x81, 0x80, 0x01]),
-    ];
+fn schemas_to_validate() -> &'static Vec<(&'static str, Value)> {
+    static SCHEMAS_TO_VALIDATE_ONCE: OnceLock<Vec<(&'static str, Value)>> = 
OnceLock::new();
+    SCHEMAS_TO_VALIDATE_ONCE.get_or_init(|| {
+        vec![
+            (r#""null""#, Value::Null),
+            (r#""boolean""#, Value::Boolean(true)),
+            (
+                r#""string""#,
+                Value::String("adsfasdf09809dsf-=adsf".to_string()),
+            ),
+            (
+                r#""bytes""#,
+                Value::Bytes("12345abcd".to_string().into_bytes()),
+            ),
+            (r#""int""#, Value::Int(1234)),
+            (r#""long""#, Value::Long(1234)),
+            (r#""float""#, Value::Float(1234.0)),
+            (r#""double""#, Value::Double(1234.0)),
+            (
+                r#"{"type": "fixed", "name": "Test", "size": 1}"#,
+                Value::Fixed(1, vec![b'B']),
+            ),
+            (
+                r#"{"type": "enum", "name": "Test", "symbols": ["A", "B"]}"#,
+                Value::Enum(1, "B".to_string()),
+            ),
+            (
+                r#"{"type": "array", "items": "long"}"#,
+                Value::Array(vec![Value::Long(1), Value::Long(3), 
Value::Long(2)]),
+            ),
+            (
+                r#"{"type": "map", "values": "long"}"#,
+                Value::Map(
+                    [
+                        ("a".to_string(), Value::Long(1i64)),
+                        ("b".to_string(), Value::Long(3i64)),
+                        ("c".to_string(), Value::Long(2i64)),
+                    ]
+                    .iter()
+                    .cloned()
+                    .collect(),
+                ),
+            ),
+            (
+                r#"["string", "null", "long"]"#,
+                Value::Union(1, Box::new(Value::Null)),
+            ),
+            (
+                r#"{"type": "record", "name": "Test", "fields": [{"name": "f", 
"type": "long"}]}"#,
+                Value::Record(vec![("f".to_string(), Value::Long(1))]),
+            ),
+        ]
+    })
+}
 
-    static ref DEFAULT_VALUE_EXAMPLES: Vec<(&'static str, &'static str, 
Value)> = vec![
-        (r#""null""#, "null", Value::Null),
-        (r#""boolean""#, "true", Value::Boolean(true)),
-        (r#""string""#, r#""foo""#, Value::String("foo".to_string())),
-        (r#""bytes""#, r#""a""#, Value::Bytes(vec![97])), // ASCII 'a' => one 
byte
-        (r#""bytes""#, r#""\u00FF""#, Value::Bytes(vec![195, 191])), // The 
value is between U+0080 and U+07FF => two bytes
-        (r#""int""#, "5", Value::Int(5)),
-        (r#""long""#, "5", Value::Long(5)),
-        (r#""float""#, "1.1", Value::Float(1.1)),
-        (r#""double""#, "1.1", Value::Double(1.1)),
-        (r#"{"type": "fixed", "name": "F", "size": 2}"#, r#""a""#, 
Value::Fixed(1, vec![97])), // ASCII 'a' => one byte
-        (r#"{"type": "fixed", "name": "F", "size": 2}"#, r#""\u00FF""#, 
Value::Fixed(2, vec![195, 191])), // The value is between U+0080 and U+07FF => 
two bytes
-        (r#"{"type": "enum", "name": "F", "symbols": ["FOO", "BAR"]}"#, 
r#""FOO""#, Value::Enum(0, "FOO".to_string())),
-        (r#"{"type": "array", "items": "int"}"#, "[1, 2, 3]", 
Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)])),
-        (r#"{"type": "map", "values": "int"}"#, r#"{"a": 1, "b": 2}"#, 
Value::Map([("a".to_string(), Value::Int(1)), ("b".to_string(), 
Value::Int(2))].iter().cloned().collect())),
-        (r#"["int", "null"]"#, "5", Value::Union(0, Box::new(Value::Int(5)))),
-        (r#"{"type": "record", "name": "F", "fields": [{"name": "A", "type": 
"int"}]}"#, r#"{"A": 5}"#,Value::Record(vec![("A".to_string(), 
Value::Int(5))])),
-        (r#"["null", "int"]"#, "null", Value::Union(0, Box::new(Value::Null))),
-    ];
+fn binary_encodings() -> &'static Vec<(i64, Vec<u8>)> {
+    static BINARY_ENCODINGS_ONCE: OnceLock<Vec<(i64, Vec<u8>)>> = 
OnceLock::new();
+    BINARY_ENCODINGS_ONCE.get_or_init(|| {
+        vec![
+            (0, vec![0x00]),
+            (-1, vec![0x01]),
+            (1, vec![0x02]),
+            (-2, vec![0x03]),
+            (2, vec![0x04]),
+            (-64, vec![0x7f]),
+            (64, vec![0x80, 0x01]),
+            (8192, vec![0x80, 0x80, 0x01]),
+            (-8193, vec![0x81, 0x80, 0x01]),
+        ]
+    })
+}
 
-    static ref LONG_RECORD_SCHEMA: Schema = Schema::parse_str(r#"
-    {
-        "type": "record",
-        "name": "Test",
-        "fields": [
-            {"name": "A", "type": "int"},
-            {"name": "B", "type": "int"},
-            {"name": "C", "type": "int"},
-            {"name": "D", "type": "int"},
-            {"name": "E", "type": "int"},
-            {"name": "F", "type": "int"},
-            {"name": "G", "type": "int"}
+fn default_value_examples() -> &'static Vec<(&'static str, &'static str, 
Value)> {
+    static DEFAULT_VALUE_EXAMPLES_ONCE: OnceLock<Vec<(&'static str, &'static 
str, Value)>> =
+        OnceLock::new();
+    DEFAULT_VALUE_EXAMPLES_ONCE.get_or_init(|| {
+        vec![
+            (r#""null""#, "null", Value::Null),
+            (r#""boolean""#, "true", Value::Boolean(true)),
+            (r#""string""#, r#""foo""#, Value::String("foo".to_string())),
+            (r#""bytes""#, r#""a""#, Value::Bytes(vec![97])), // ASCII 'a' => 
one byte
+            (r#""bytes""#, r#""\u00FF""#, Value::Bytes(vec![195, 191])), // 
The value is between U+0080 and U+07FF => two bytes
+            (r#""int""#, "5", Value::Int(5)),
+            (r#""long""#, "5", Value::Long(5)),
+            (r#""float""#, "1.1", Value::Float(1.1)),
+            (r#""double""#, "1.1", Value::Double(1.1)),
+            (
+                r#"{"type": "fixed", "name": "F", "size": 2}"#,
+                r#""a""#,
+                Value::Fixed(1, vec![97]),
+            ), // ASCII 'a' => one byte
+            (
+                r#"{"type": "fixed", "name": "F", "size": 2}"#,
+                r#""\u00FF""#,
+                Value::Fixed(2, vec![195, 191]),
+            ), // The value is between U+0080 and U+07FF => two bytes
+            (
+                r#"{"type": "enum", "name": "F", "symbols": ["FOO", "BAR"]}"#,
+                r#""FOO""#,
+                Value::Enum(0, "FOO".to_string()),
+            ),
+            (
+                r#"{"type": "array", "items": "int"}"#,
+                "[1, 2, 3]",
+                Value::Array(vec![Value::Int(1), Value::Int(2), 
Value::Int(3)]),
+            ),
+            (
+                r#"{"type": "map", "values": "int"}"#,
+                r#"{"a": 1, "b": 2}"#,
+                Value::Map(
+                    [
+                        ("a".to_string(), Value::Int(1)),
+                        ("b".to_string(), Value::Int(2)),
+                    ]
+                    .iter()
+                    .cloned()
+                    .collect(),
+                ),
+            ),
+            (
+                r#"["int", "null"]"#,
+                "5",
+                Value::Union(0, Box::new(Value::Int(5))),
+            ),
+            (
+                r#"{"type": "record", "name": "F", "fields": [{"name": "A", 
"type": "int"}]}"#,
+                r#"{"A": 5}"#,
+                Value::Record(vec![("A".to_string(), Value::Int(5))]),
+            ),
+            (
+                r#"["null", "int"]"#,
+                "null",
+                Value::Union(0, Box::new(Value::Null)),
+            ),
         ]
-    }
-    "#).unwrap();
+    })
+}
 
-    static ref LONG_RECORD_DATUM: Value = Value::Record(vec![
-        ("A".to_string(), Value::Int(1)),
-        ("B".to_string(), Value::Int(2)),
-        ("C".to_string(), Value::Int(3)),
-        ("D".to_string(), Value::Int(4)),
-        ("E".to_string(), Value::Int(5)),
-        ("F".to_string(), Value::Int(6)),
-        ("G".to_string(), Value::Int(7)),
-    ]);
+fn long_record_schema() -> &'static Schema {
+    static LONG_RECORD_SCHEMA_ONCE: OnceLock<Schema> = OnceLock::new();
+    LONG_RECORD_SCHEMA_ONCE.get_or_init(|| {
+        Schema::parse_str(
+            r#"
+{
+    "type": "record",
+    "name": "Test",
+    "fields": [
+        {"name": "A", "type": "int"},
+        {"name": "B", "type": "int"},
+        {"name": "C", "type": "int"},
+        {"name": "D", "type": "int"},
+        {"name": "E", "type": "int"},
+        {"name": "F", "type": "int"},
+        {"name": "G", "type": "int"}
+    ]
+}
+"#,
+        )
+        .unwrap()
+    })
+}
+
+fn long_record_datum() -> &'static Value {
+    static LONG_RECORD_DATUM_ONCE: OnceLock<Value> = OnceLock::new();
+    LONG_RECORD_DATUM_ONCE.get_or_init(|| {
+        Value::Record(vec![
+            ("A".to_string(), Value::Int(1)),
+            ("B".to_string(), Value::Int(2)),
+            ("C".to_string(), Value::Int(3)),
+            ("D".to_string(), Value::Int(4)),
+            ("E".to_string(), Value::Int(5)),
+            ("F".to_string(), Value::Int(6)),
+            ("G".to_string(), Value::Int(7)),
+        ])
+    })
 }
 
 #[test]
 fn test_validate() -> TestResult {
-    for (raw_schema, value) in SCHEMAS_TO_VALIDATE.iter() {
+    for (raw_schema, value) in schemas_to_validate().iter() {
         let schema = Schema::parse_str(raw_schema)?;
         assert!(
             value.validate(&schema),
@@ -114,7 +213,7 @@ fn test_validate() -> TestResult {
 
 #[test]
 fn test_round_trip() -> TestResult {
-    for (raw_schema, value) in SCHEMAS_TO_VALIDATE.iter() {
+    for (raw_schema, value) in schemas_to_validate().iter() {
         let schema = Schema::parse_str(raw_schema)?;
         let encoded = to_avro_datum(&schema, value.clone()).unwrap();
         let decoded = from_avro_datum(&schema, &mut Cursor::new(encoded), 
None).unwrap();
@@ -126,7 +225,7 @@ fn test_round_trip() -> TestResult {
 
 #[test]
 fn test_binary_int_encoding() -> TestResult {
-    for (number, hex_encoding) in BINARY_ENCODINGS.iter() {
+    for (number, hex_encoding) in binary_encodings().iter() {
         let encoded = to_avro_datum(&Schema::Int, Value::Int(*number as i32))?;
         assert_eq!(&encoded, hex_encoding);
     }
@@ -136,7 +235,7 @@ fn test_binary_int_encoding() -> TestResult {
 
 #[test]
 fn test_binary_long_encoding() -> TestResult {
-    for (number, hex_encoding) in BINARY_ENCODINGS.iter() {
+    for (number, hex_encoding) in binary_encodings().iter() {
         let encoded = to_avro_datum(&Schema::Long, Value::Long(*number))?;
         assert_eq!(&encoded, hex_encoding);
     }
@@ -196,7 +295,7 @@ fn test_unknown_symbol() -> TestResult {
 
 #[test]
 fn test_default_value() -> TestResult {
-    for (field_type, default_json, default_datum) in 
DEFAULT_VALUE_EXAMPLES.iter() {
+    for (field_type, default_json, default_datum) in 
default_value_examples().iter() {
         let reader_schema = Schema::parse_str(&format!(
             r#"{{
                 "type": "record",
@@ -207,9 +306,9 @@ fn test_default_value() -> TestResult {
             }}"#
         ))?;
         let datum_to_read = Value::Record(vec![("H".to_string(), 
default_datum.clone())]);
-        let encoded = to_avro_datum(&LONG_RECORD_SCHEMA, 
LONG_RECORD_DATUM.clone())?;
+        let encoded = to_avro_datum(long_record_schema(), 
long_record_datum().clone())?;
         let datum_read = from_avro_datum(
-            &LONG_RECORD_SCHEMA,
+            long_record_schema(),
             &mut Cursor::new(encoded),
             Some(&reader_schema),
         )?;
@@ -234,9 +333,9 @@ fn test_no_default_value() -> TestResult {
             ]
         }"#,
     )?;
-    let encoded = to_avro_datum(&LONG_RECORD_SCHEMA, 
LONG_RECORD_DATUM.clone())?;
+    let encoded = to_avro_datum(long_record_schema(), 
long_record_datum().clone())?;
     let result = from_avro_datum(
-        &LONG_RECORD_SCHEMA,
+        long_record_schema(),
         &mut Cursor::new(encoded),
         Some(&reader_schema),
     );
@@ -263,9 +362,9 @@ fn test_projection() -> TestResult {
         ("E".to_string(), Value::Int(5)),
         ("F".to_string(), Value::Int(6)),
     ]);
-    let encoded = to_avro_datum(&LONG_RECORD_SCHEMA, 
LONG_RECORD_DATUM.clone())?;
+    let encoded = to_avro_datum(long_record_schema(), 
long_record_datum().clone())?;
     let datum_read = from_avro_datum(
-        &LONG_RECORD_SCHEMA,
+        long_record_schema(),
         &mut Cursor::new(encoded),
         Some(&reader_schema),
     )?;
@@ -292,9 +391,9 @@ fn test_field_order() -> TestResult {
         ("F".to_string(), Value::Int(6)),
         ("E".to_string(), Value::Int(5)),
     ]);
-    let encoded = to_avro_datum(&LONG_RECORD_SCHEMA, 
LONG_RECORD_DATUM.clone())?;
+    let encoded = to_avro_datum(long_record_schema(), 
long_record_datum().clone())?;
     let datum_read = from_avro_datum(
-        &LONG_RECORD_SCHEMA,
+        long_record_schema(),
         &mut Cursor::new(encoded),
         Some(&reader_schema),
     )?;
diff --git a/lang/rust/avro/tests/schema.rs b/lang/rust/avro/tests/schema.rs
index 223305876..3d2db1a33 100644
--- a/lang/rust/avro/tests/schema.rs
+++ b/lang/rust/avro/tests/schema.rs
@@ -15,7 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use std::io::{Cursor, Read};
+use std::{
+    io::{Cursor, Read},
+    sync::OnceLock,
+};
 
 use apache_avro::{
     from_avro_datum, from_value,
@@ -25,7 +28,6 @@ use apache_avro::{
     Codec, Error, Reader, Schema, Writer,
 };
 use apache_avro_test_helper::{init, TestResult};
-use lazy_static::lazy_static;
 
 const PRIMITIVE_EXAMPLES: &[(&str, bool)] = &[
     (r#""null""#, true),
@@ -589,29 +591,35 @@ const TIMESTAMPMICROS_LOGICAL_TYPE: &[(&str, bool)] = &[
     ),
 ];
 
-lazy_static! {
-    static ref EXAMPLES: Vec<(&'static str, bool)> = Vec::new()
-        .iter()
-        .copied()
-        .chain(PRIMITIVE_EXAMPLES.iter().copied())
-        .chain(FIXED_EXAMPLES.iter().copied())
-        .chain(ENUM_EXAMPLES.iter().copied())
-        .chain(ARRAY_EXAMPLES.iter().copied())
-        .chain(MAP_EXAMPLES.iter().copied())
-        .chain(UNION_EXAMPLES.iter().copied())
-        .chain(RECORD_EXAMPLES.iter().copied())
-        .chain(DOC_EXAMPLES.iter().copied())
-        .chain(OTHER_ATTRIBUTES_EXAMPLES.iter().copied())
-        .chain(DECIMAL_LOGICAL_TYPE.iter().copied())
-        .chain(DECIMAL_LOGICAL_TYPE_ATTRIBUTES.iter().copied())
-        .chain(DATE_LOGICAL_TYPE.iter().copied())
-        .chain(TIMEMILLIS_LOGICAL_TYPE.iter().copied())
-        .chain(TIMEMICROS_LOGICAL_TYPE.iter().copied())
-        .chain(TIMESTAMPMILLIS_LOGICAL_TYPE.iter().copied())
-        .chain(TIMESTAMPMICROS_LOGICAL_TYPE.iter().copied())
-        .collect();
-    static ref VALID_EXAMPLES: Vec<(&'static str, bool)> =
-        EXAMPLES.iter().copied().filter(|s| s.1).collect();
+fn examples() -> &'static Vec<(&'static str, bool)> {
+    static EXAMPLES_ONCE: OnceLock<Vec<(&'static str, bool)>> = 
OnceLock::new();
+    EXAMPLES_ONCE.get_or_init(|| {
+        Vec::new()
+            .iter()
+            .copied()
+            .chain(PRIMITIVE_EXAMPLES.iter().copied())
+            .chain(FIXED_EXAMPLES.iter().copied())
+            .chain(ENUM_EXAMPLES.iter().copied())
+            .chain(ARRAY_EXAMPLES.iter().copied())
+            .chain(MAP_EXAMPLES.iter().copied())
+            .chain(UNION_EXAMPLES.iter().copied())
+            .chain(RECORD_EXAMPLES.iter().copied())
+            .chain(DOC_EXAMPLES.iter().copied())
+            .chain(OTHER_ATTRIBUTES_EXAMPLES.iter().copied())
+            .chain(DECIMAL_LOGICAL_TYPE.iter().copied())
+            .chain(DECIMAL_LOGICAL_TYPE_ATTRIBUTES.iter().copied())
+            .chain(DATE_LOGICAL_TYPE.iter().copied())
+            .chain(TIMEMILLIS_LOGICAL_TYPE.iter().copied())
+            .chain(TIMEMICROS_LOGICAL_TYPE.iter().copied())
+            .chain(TIMESTAMPMILLIS_LOGICAL_TYPE.iter().copied())
+            .chain(TIMESTAMPMICROS_LOGICAL_TYPE.iter().copied())
+            .collect()
+    })
+}
+
+fn valid_examples() -> &'static Vec<(&'static str, bool)> {
+    static VALID_EXAMPLES_ONCE: OnceLock<Vec<(&'static str, bool)>> = 
OnceLock::new();
+    VALID_EXAMPLES_ONCE.get_or_init(|| examples().iter().copied().filter(|s| 
s.1).collect())
 }
 
 #[test]
@@ -668,7 +676,7 @@ fn test_correct_recursive_extraction() -> TestResult {
 #[test]
 fn test_parse() -> TestResult {
     init();
-    for (raw_schema, valid) in EXAMPLES.iter() {
+    for (raw_schema, valid) in examples().iter() {
         let schema = Schema::parse_str(raw_schema);
         if *valid {
             assert!(
@@ -688,7 +696,7 @@ fn test_parse() -> TestResult {
 #[test]
 fn test_3799_parse_reader() -> TestResult {
     init();
-    for (raw_schema, valid) in EXAMPLES.iter() {
+    for (raw_schema, valid) in examples().iter() {
         let schema = Schema::parse_reader(&mut Cursor::new(raw_schema));
         if *valid {
             assert!(
@@ -704,7 +712,7 @@ fn test_3799_parse_reader() -> TestResult {
     }
 
     // Ensure it works for trait objects too.
-    for (raw_schema, valid) in EXAMPLES.iter() {
+    for (raw_schema, valid) in examples().iter() {
         let reader: &mut dyn Read = &mut Cursor::new(raw_schema);
         let schema = Schema::parse_reader(reader);
         if *valid {
@@ -744,7 +752,7 @@ fn test_3799_raise_io_error_from_parse_read() -> Result<(), 
String> {
 /// Test that the string generated by an Avro Schema object is, in fact, a 
valid Avro schema.
 fn test_valid_cast_to_string_after_parse() -> TestResult {
     init();
-    for (raw_schema, _) in VALID_EXAMPLES.iter() {
+    for (raw_schema, _) in valid_examples().iter() {
         let schema = Schema::parse_str(raw_schema)?;
         Schema::parse_str(schema.canonical_form().as_str())?;
     }
@@ -757,7 +765,7 @@ fn test_valid_cast_to_string_after_parse() -> TestResult {
 /// 3. Ensure "original" and "round trip" schemas are equivalent.
 fn test_equivalence_after_round_trip() -> TestResult {
     init();
-    for (raw_schema, _) in VALID_EXAMPLES.iter() {
+    for (raw_schema, _) in valid_examples().iter() {
         let original_schema = Schema::parse_str(raw_schema)?;
         let round_trip_schema = 
Schema::parse_str(original_schema.canonical_form().as_str())?;
         assert_eq!(original_schema, round_trip_schema);
diff --git a/lang/rust/avro_test_helper/Cargo.toml 
b/lang/rust/avro_test_helper/Cargo.toml
index 3330dce24..f5a77125f 100644
--- a/lang/rust/avro_test_helper/Cargo.toml
+++ b/lang/rust/avro_test_helper/Cargo.toml
@@ -35,6 +35,5 @@ anyhow = { default-features = false, version = "1.0.75", 
features = ["std"] }
 color-backtrace = { default-features = false, version = "0.5.1" }
 ctor = { default-features = false, version = "0.2.4" }
 env_logger = { default-features = false, version = "0.10.0" }
-lazy_static = { default-features = false, version = "1.4.0" }
 log = { default-features = false, version = "0.4.20" }
 ref_thread_local = { default-features = false, version = "0.1.1" }
diff --git a/lang/rust/avro_test_helper/src/logger.rs 
b/lang/rust/avro_test_helper/src/logger.rs
index 09fc1bede..505e42541 100644
--- a/lang/rust/avro_test_helper/src/logger.rs
+++ b/lang/rust/avro_test_helper/src/logger.rs
@@ -16,9 +16,9 @@
 // under the License.
 
 use crate::LOG_MESSAGES;
-use lazy_static::lazy_static;
 use log::{LevelFilter, Log, Metadata};
 use ref_thread_local::RefThreadLocal;
+use std::sync::OnceLock;
 
 struct TestLogger {
     delegate: env_logger::Logger,
@@ -41,14 +41,15 @@ impl Log for TestLogger {
     fn flush(&self) {}
 }
 
-lazy_static! {
+fn test_logger() -> &'static TestLogger {
     // Lazy static because the Logger has to be 'static
-    static ref TEST_LOGGER: TestLogger = TestLogger {
+    static TEST_LOGGER_ONCE: OnceLock<TestLogger> = OnceLock::new();
+    TEST_LOGGER_ONCE.get_or_init(|| TestLogger {
         delegate: env_logger::Builder::from_default_env()
             .filter_level(LevelFilter::Off)
             .parse_default_env()
             .build(),
-    };
+    })
 }
 
 pub fn clear_log_messages() {
@@ -70,7 +71,7 @@ pub fn assert_logged(expected_message: &str) {
 
 #[cfg(not(target_arch = "wasm32"))]
 pub(crate) fn install() {
-    log::set_logger(&*TEST_LOGGER)
+    log::set_logger(test_logger())
         .map(|_| log::set_max_level(LevelFilter::Trace))
         .map_err(|err| {
             eprintln!("Failed to set the custom logger: {err:?}");

Reply via email to