This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/main by this push:
new 92512b20e AVRO-3839: [Rust] Replace lazy_static crate with
std::sync::OnceLock (#2461)
92512b20e is described below
commit 92512b20ec906b9912751a689f4d26f852485839
Author: Martin Grigorov <[email protected]>
AuthorDate: Mon Dec 11 16:00:45 2023 +0200
AVRO-3839: [Rust] Replace lazy_static crate with std::sync::OnceLock (#2461)
* AVRO-3839: [Rust] Replace lazy_static crate with std::sync::OnceLock
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
* AVRO-3839: [Rust] Bump minimal Rust version to 1.70.0
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---------
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---
.github/workflows/test-lang-rust-ci.yml | 4 +-
.github/workflows/test-lang-rust-clippy.yml | 2 +-
lang/rust/Cargo.lock | 2 -
lang/rust/Cargo.toml | 3 +-
lang/rust/avro/Cargo.toml | 1 -
lang/rust/avro/README.md | 2 +-
lang/rust/avro/src/rabin.rs | 13 +-
lang/rust/avro/src/schema.rs | 48 +++--
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 +-
share/docker/Dockerfile | 2 +-
13 files changed, 273 insertions(+), 155 deletions(-)
diff --git a/.github/workflows/test-lang-rust-ci.yml
b/.github/workflows/test-lang-rust-ci.yml
index 8c78fab8d..d41c52308 100644
--- a/.github/workflows/test-lang-rust-ci.yml
+++ b/.github/workflows/test-lang-rust-ci.yml
@@ -50,7 +50,7 @@ jobs:
- 'stable'
- 'beta'
- 'nightly'
- - '1.65.0' # MSRV
+ - '1.70.0' # MSRV
target:
- x86_64-unknown-linux-gnu
- wasm32-unknown-unknown
@@ -273,4 +273,4 @@ jobs:
- name: Build
run: |
set -x
- ./build.sh test
\ No newline at end of file
+ ./build.sh test
diff --git a/.github/workflows/test-lang-rust-clippy.yml
b/.github/workflows/test-lang-rust-clippy.yml
index b8c6fe90d..01b6b1863 100644
--- a/.github/workflows/test-lang-rust-clippy.yml
+++ b/.github/workflows/test-lang-rust-clippy.yml
@@ -47,7 +47,7 @@ jobs:
matrix:
rust:
- 'stable'
- - '1.65.0' # MSRV
+ - '1.70.0' # MSRV
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
diff --git a/lang/rust/Cargo.lock b/lang/rust/Cargo.lock
index 9a8330ab5..30b408cf4 100644
--- a/lang/rust/Cargo.lock
+++ b/lang/rust/Cargo.lock
@@ -75,7 +75,6 @@ dependencies = [
"criterion",
"digest",
"hex-literal",
- "lazy_static",
"libflate",
"log",
"md-5",
@@ -120,7 +119,6 @@ dependencies = [
"better-panic",
"ctor",
"env_logger",
- "lazy_static",
"log",
"ref_thread_local",
]
diff --git a/lang/rust/Cargo.toml b/lang/rust/Cargo.toml
index 0494d9c1e..23ee3249f 100644
--- a/lang/rust/Cargo.toml
+++ b/lang/rust/Cargo.toml
@@ -34,14 +34,13 @@ authors = ["Apache Avro team <[email protected]>"]
license = "Apache-2.0"
repository = "https://github.com/apache/avro"
edition = "2021"
-rust-version = "1.65.0"
+rust-version = "1.70.0"
keywords = ["avro", "data", "serialization"]
categories = ["encoding"]
documentation = "https://docs.rs/apache-avro"
# dependencies used by more than one members
[workspace.dependencies]
-lazy_static = { default-features = false, version = "1.4.0" }
log = { default-features = false, version = "0.4.20" }
serde = { default-features = false, version = "1.0.193", features = ["derive"]
}
serde_json = { default-features = false, version = "1.0.108", features =
["std"] }
diff --git a/lang/rust/avro/Cargo.toml b/lang/rust/avro/Cargo.toml
index ac52435d8..6568388e0 100644
--- a/lang/rust/avro/Cargo.toml
+++ b/lang/rust/avro/Cargo.toml
@@ -59,7 +59,6 @@ bigdecimal = { default-features = false, version = "0.4.2",
features = ["std"] }
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 = { workspace = true }
libflate = { default-features = false, version = "2.0.0", features = ["std"] }
log = { workspace = true }
num-bigint = { default-features = false, version = "0.4.4" }
diff --git a/lang/rust/avro/README.md b/lang/rust/avro/README.md
index bb3fd2b2e..75789964e 100644
--- a/lang/rust/avro/README.md
+++ b/lang/rust/avro/README.md
@@ -656,7 +656,7 @@ assert!(SchemaCompatibility::can_read(&writers_schema,
&readers_schema).is_err()
## Minimal supported Rust version
-1.65.0
+1.70.0
## License
This project is licensed under [Apache License
2.0](https://github.com/apache/avro/blob/main/LICENSE.txt).
diff --git a/lang/rust/avro/src/rabin.rs b/lang/rust/avro/src/rabin.rs
index e742346e3..f8f516477 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 90cdeb1b2..258ea87b0 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_lite::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_]*(\.[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_]*(\.[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
@@ -252,9 +268,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()),
@@ -285,10 +301,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(),
));
}
}
@@ -657,7 +673,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));
}
@@ -1617,7 +1633,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()));
}
@@ -6193,7 +6209,7 @@ mod tests {
let name = Name::new(full_name);
assert!(name.is_err());
let expected =
- Error::InvalidSchemaName(full_name.to_string(),
SCHEMA_NAME_R.as_str()).to_string();
+ Error::InvalidSchemaName(full_name.to_string(),
schema_name_r().as_str()).to_string();
let err = name.map_err(|e| e.to_string()).err().unwrap();
assert_eq!(expected, err);
@@ -6201,7 +6217,7 @@ mod tests {
let name = Name::new(full_name);
assert!(name.is_err());
let expected =
- Error::InvalidSchemaName(full_name.to_string(),
SCHEMA_NAME_R.as_str()).to_string();
+ Error::InvalidSchemaName(full_name.to_string(),
schema_name_r().as_str()).to_string();
let err = name.map_err(|e| e.to_string()).err().unwrap();
assert_eq!(expected, err);
Ok(())
diff --git a/lang/rust/avro/tests/io.rs b/lang/rust/avro/tests/io.rs
index ab3712893..03442ed7f 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 d548281da..7851d957d 100644
--- a/lang/rust/avro/tests/schema.rs
+++ b/lang/rust/avro/tests/schema.rs
@@ -18,6 +18,7 @@
use std::{
collections::HashMap,
io::{Cursor, Read},
+ sync::OnceLock,
};
use apache_avro::{
@@ -28,7 +29,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),
@@ -628,31 +628,37 @@ const LOCAL_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())
- .chain(LOCAL_TIMESTAMPMILLIS_LOGICAL_TYPE.iter().copied())
- .chain(LOCAL_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())
+ .chain(LOCAL_TIMESTAMPMILLIS_LOGICAL_TYPE.iter().copied())
+ .chain(LOCAL_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]
@@ -709,7 +715,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!(
@@ -729,7 +735,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!(
@@ -745,7 +751,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 {
@@ -785,7 +791,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())?;
}
@@ -798,7 +804,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 7f3198951..f88ce6d2c 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"] }
better-panic = { default-features = false, version = "0.3.0" }
ctor = { default-features = false, version = "0.2.5" }
env_logger = { default-features = false, version = "0.10.1" }
-lazy_static = { workspace = true }
log = { workspace = true }
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:?}");
diff --git a/share/docker/Dockerfile b/share/docker/Dockerfile
index 4973fbe8e..8cae135d8 100644
--- a/share/docker/Dockerfile
+++ b/share/docker/Dockerfile
@@ -211,7 +211,7 @@ RUN gem install bundler --no-document && \
cd /tmp/lang/ruby && bundle install
# Install Rust
-RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
--default-toolchain 1.65.0
+RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
--default-toolchain 1.70.0
# Note: This "ubertool" container has two JDK versions:
# - OpenJDK 8