diff --git a/godot-core/src/builtin/string/gstring.rs b/godot-core/src/builtin/string/gstring.rs index 1585b7e6c..8f12b256d 100644 --- a/godot-core/src/builtin/string/gstring.rs +++ b/godot-core/src/builtin/string/gstring.rs @@ -336,6 +336,21 @@ impl fmt::Debug for GString { } } +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Comparison with Rust strings + +// API design: +// * StringName and NodePath don't implement PartialEq<&str> yet, because they require allocation (convert to GString). +// == should ideally not allocate. +// * Reverse `impl PartialEq for &str` is not implemented now. Comparisons usually take the form of variable == "literal". +// Can be added later if there are good use-cases. + +impl PartialEq<&str> for GString { + fn eq(&self, other: &&str) -> bool { + self.chars().iter().copied().eq(other.chars()) + } +} + // ---------------------------------------------------------------------------------------------------------------------------------------------- // Conversion from/into Rust string-types diff --git a/itest/rust/build.rs b/itest/rust/build.rs index d664baaaa..eb7c0d197 100644 --- a/itest/rust/build.rs +++ b/itest/rust/build.rs @@ -120,7 +120,7 @@ fn collect_inputs() -> Vec { push!(inputs; float, f64, 127.83156478); push!(inputs; bool, bool, true); push!(inputs; Color, Color, Color(0.7, 0.5, 0.3, 0.2), Color::from_rgba(0.7, 0.5, 0.3, 0.2)); - push!(inputs; String, GString, "hello", "hello".into()); + push!(inputs; String, GString, "hello", GString::from("hello")); push!(inputs; StringName, StringName, &"hello", "hello".into()); pushs!(inputs; NodePath, NodePath, r#"^"hello""#, "hello".into(), true, true, None); push!(inputs; Vector2, Vector2, Vector2(12.5, -3.5), Vector2::new(12.5, -3.5)); @@ -170,7 +170,7 @@ fn collect_inputs() -> Vec { push_newtype!(inputs; float, NewF64(f64), 127.83156478); push_newtype!(inputs; bool, NewBool(bool), true); push_newtype!(inputs; Color, NewColor(Color), Color(0.7, 0.5, 0.3, 0.2), NewColor(Color::from_rgba(0.7, 0.5, 0.3, 0.2))); - push_newtype!(inputs; String, NewString(GString), "hello", NewString("hello".into())); + push_newtype!(inputs; String, NewString(GString), "hello", NewString(GString::from("hello"))); push_newtype!(inputs; StringName, NewStringName(StringName), &"hello", NewStringName("hello".into())); push_newtype!(@s inputs; NodePath, NewNodePath(NodePath), r#"^"hello""#, NewNodePath("hello".into())); push_newtype!(inputs; Vector2, NewVector2(Vector2), Vector2(12.5, -3.5), NewVector2(Vector2::new(12.5, -3.5))); diff --git a/itest/rust/src/builtin_tests/containers/packed_array_test.rs b/itest/rust/src/builtin_tests/containers/packed_array_test.rs index b8a2fe973..e3c04b65b 100644 --- a/itest/rust/src/builtin_tests/containers/packed_array_test.rs +++ b/itest/rust/src/builtin_tests/containers/packed_array_test.rs @@ -107,11 +107,11 @@ fn packed_array_index() { array.push("first"); array.push(&GString::from("second")); - assert_eq!(array[0], "first".into()); - assert_eq!(array[1], "second".into()); + assert_eq!(array[0], "first"); + assert_eq!(array[1], "second"); array[0] = GString::from("begin"); - assert_eq!(array[0], "begin".into()); + assert_eq!(array[0], "begin"); } #[itest] @@ -202,7 +202,7 @@ fn packed_array_push() { let mut strings = PackedStringArray::from(&[GString::from("a")]); strings.push("b"); assert_eq!(strings.len(), 2); - assert_eq!(strings[1], "b".into()); + assert_eq!(strings[1], "b"); fn test() { let mut array = PackedArray::::new(); @@ -481,7 +481,7 @@ fn packed_array_as_slice() { ); let empty = PackedStringArray::new(); - assert_eq!(empty.as_slice(), &[]); + assert_eq!(empty.as_slice(), &[] as &[GString]); // Ambiguity due to GString==&str op. Could use is_empty() but this uses explicit type. } #[itest] @@ -501,7 +501,7 @@ fn packed_array_as_mut_slice() { ); let mut empty = PackedStringArray::new(); - assert_eq!(empty.as_mut_slice(), &mut []); + assert_eq!(empty.as_mut_slice(), &mut [] as &mut [GString]); } // ---------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/itest/rust/src/builtin_tests/string/gstring_test.rs b/itest/rust/src/builtin_tests/string/gstring_test.rs index 84610a8c5..9162313d4 100644 --- a/itest/rust/src/builtin_tests/string/gstring_test.rs +++ b/itest/rust/src/builtin_tests/string/gstring_test.rs @@ -41,6 +41,13 @@ fn string_equality() { assert_ne!(string, different); } +#[itest] +fn string_eq_str() { + let gstring = GString::from("hello"); + assert_eq!(gstring, "hello"); + assert_ne!(gstring, "hallo"); +} + #[itest] fn string_ordering() { let low = GString::from("Alpha"); @@ -130,12 +137,12 @@ fn string_with_null() { #[itest] fn string_substr() { let string = GString::from("stable"); - assert_eq!(string.substr(..), "stable".into()); - assert_eq!(string.substr(1..), "table".into()); - assert_eq!(string.substr(..4), "stab".into()); - assert_eq!(string.substr(..=3), "stab".into()); - assert_eq!(string.substr(2..5), "abl".into()); - assert_eq!(string.substr(2..=4), "abl".into()); + assert_eq!(string.substr(..), "stable"); + assert_eq!(string.substr(1..), "table"); + assert_eq!(string.substr(..4), "stab"); + assert_eq!(string.substr(..=3), "stab"); + assert_eq!(string.substr(2..5), "abl"); + assert_eq!(string.substr(2..=4), "abl"); } #[itest] @@ -217,42 +224,42 @@ fn gstring_erase() { let s = GString::from("Hello World"); assert_eq!(s.erase(..), GString::new()); assert_eq!(s.erase(4..4), s); - assert_eq!(s.erase(2..=2), "Helo World".into()); - assert_eq!(s.erase(1..=3), "Ho World".into()); - assert_eq!(s.erase(1..4), "Ho World".into()); - assert_eq!(s.erase(..6), "World".into()); - assert_eq!(s.erase(5..), "Hello".into()); + assert_eq!(s.erase(2..=2), "Helo World"); + assert_eq!(s.erase(1..=3), "Ho World"); + assert_eq!(s.erase(1..4), "Ho World"); + assert_eq!(s.erase(..6), "World"); + assert_eq!(s.erase(5..), "Hello"); } #[itest] fn gstring_insert() { let s = GString::from("H World"); - assert_eq!(s.insert(1, "i"), "Hi World".into()); - assert_eq!(s.insert(1, "ello"), "Hello World".into()); - assert_eq!(s.insert(7, "."), "H World.".into()); - assert_eq!(s.insert(0, "¿"), "¿H World".into()); + assert_eq!(s.insert(1, "i"), "Hi World"); + assert_eq!(s.insert(1, "ello"), "Hello World"); + assert_eq!(s.insert(7, "."), "H World."); + assert_eq!(s.insert(0, "¿"), "¿H World"); // Special behavior in Godot, but maybe the idea is to allow large constants to mean "end". - assert_eq!(s.insert(123, "!"), "H World!".into()); + assert_eq!(s.insert(123, "!"), "H World!"); } #[itest] fn gstring_pad() { let s = GString::from("123"); - assert_eq!(s.lpad(5, '0'), "00123".into()); - assert_eq!(s.lpad(2, ' '), "123".into()); - assert_eq!(s.lpad(4, ' '), " 123".into()); + assert_eq!(s.lpad(5, '0'), "00123"); + assert_eq!(s.lpad(2, ' '), "123"); + assert_eq!(s.lpad(4, ' '), " 123"); - assert_eq!(s.rpad(5, '+'), "123++".into()); - assert_eq!(s.rpad(2, ' '), "123".into()); - assert_eq!(s.rpad(4, ' '), "123 ".into()); + assert_eq!(s.rpad(5, '+'), "123++"); + assert_eq!(s.rpad(2, ' '), "123"); + assert_eq!(s.rpad(4, ' '), "123 "); let s = GString::from("123.456"); - assert_eq!(s.pad_decimals(5), "123.45600".into()); - assert_eq!(s.pad_decimals(2), "123.45".into()); // note: Godot rounds down + assert_eq!(s.pad_decimals(5), "123.45600"); + assert_eq!(s.pad_decimals(2), "123.45"); // note: Godot rounds down - assert_eq!(s.pad_zeros(5), "00123.456".into()); - assert_eq!(s.pad_zeros(2), "123.456".into()); + assert_eq!(s.pad_zeros(5), "00123.456"); + assert_eq!(s.pad_zeros(2), "123.456"); } // Byte and C-string conversions. diff --git a/itest/rust/src/engine_tests/match_class_test.rs b/itest/rust/src/engine_tests/match_class_test.rs index 3746e6d54..4e8f7e87b 100644 --- a/itest/rust/src/engine_tests/match_class_test.rs +++ b/itest/rust/src/engine_tests/match_class_test.rs @@ -126,7 +126,7 @@ fn match_class_named_fallback_matched() { // Named fallback with access to original object. other => { require_object(&other); - assert_eq!(other.get_class(), "Resource".into()); + assert_eq!(other.get_class(), "Resource"); 3 } }; @@ -145,7 +145,7 @@ fn match_class_named_mut_fallback_matched() { // Named fallback with access to original object. mut other => { require_mut_object(&mut other); - assert_eq!(other.get_class(), "Resource".into()); + assert_eq!(other.get_class(), "Resource"); 3 } }; diff --git a/itest/rust/src/engine_tests/utilities_test.rs b/itest/rust/src/engine_tests/utilities_test.rs index 607e947a3..c2ed66d0e 100644 --- a/itest/rust/src/engine_tests/utilities_test.rs +++ b/itest/rust/src/engine_tests/utilities_test.rs @@ -39,8 +39,7 @@ fn utilities_str() { let empty = str(&[]); - // TODO: implement GString==&str operator. Then look for "...".into() patterns and replace them. - assert_eq!(concat, "12 is a true number".into()); + assert_eq!(concat, "12 is a true number"); assert_eq!(concat, godot_str!("{a}{b}{c}{d}")); assert_eq!(empty, GString::new()); } diff --git a/itest/rust/src/object_tests/base_test.rs b/itest/rust/src/object_tests/base_test.rs index 52ca2b2bc..05108c31c 100644 --- a/itest/rust/src/object_tests/base_test.rs +++ b/itest/rust/src/object_tests/base_test.rs @@ -176,7 +176,7 @@ fn base_refcounted_weak_reference() { // Call an API to ensure Base is functional. let class_name = base_guard.get_class(); - assert_eq!(class_name, "RefcBased".into()); + assert_eq!(class_name, "RefcBased"); } let final_refcount = obj.get_reference_count(); diff --git a/itest/rust/src/object_tests/property_test.rs b/itest/rust/src/object_tests/property_test.rs index b506e10a9..07f42b03c 100644 --- a/itest/rust/src/object_tests/property_test.rs +++ b/itest/rust/src/object_tests/property_test.rs @@ -414,14 +414,11 @@ fn derive_property() { fn enum_var_hint() { let int_prop = ::var_hint(); assert_eq!(int_prop.hint, PropertyHint::ENUM); - assert_eq!( - int_prop.hint_string, - "Peaceful:0,Defend:1,Aggressive:7".into() - ); + assert_eq!(int_prop.hint_string, "Peaceful:0,Defend:1,Aggressive:7"); let str_prop = ::var_hint(); assert_eq!(str_prop.hint, PropertyHint::ENUM); - assert_eq!(str_prop.hint_string, "Peaceful,Defend,Aggressive".into()); + assert_eq!(str_prop.hint_string, "Peaceful,Defend,Aggressive"); } #[derive(GodotClass)] diff --git a/itest/rust/src/register_tests/derive_godotconvert_test.rs b/itest/rust/src/register_tests/derive_godotconvert_test.rs index 6e61a7009..87d5bee6a 100644 --- a/itest/rust/src/register_tests/derive_godotconvert_test.rs +++ b/itest/rust/src/register_tests/derive_godotconvert_test.rs @@ -77,12 +77,12 @@ fn enum_stringy() { roundtrip(EnumStringy::E); roundtrip(EnumStringy::F); - assert_eq!(EnumStringy::A.to_godot(), "A".into()); - assert_eq!(EnumStringy::B.to_godot(), "B".into()); - assert_eq!(EnumStringy::C.to_godot(), "C".into()); - assert_eq!(EnumStringy::D.to_godot(), "D".into()); - assert_eq!(EnumStringy::E.to_godot(), "E".into()); - assert_eq!(EnumStringy::F.to_godot(), "F".into()); + assert_eq!(EnumStringy::A.to_godot(), "A"); + assert_eq!(EnumStringy::B.to_godot(), "B"); + assert_eq!(EnumStringy::C.to_godot(), "C"); + assert_eq!(EnumStringy::D.to_godot(), "D"); + assert_eq!(EnumStringy::E.to_godot(), "E"); + assert_eq!(EnumStringy::F.to_godot(), "F"); // Rust-side discriminants. assert_eq!(EnumStringy::A as isize, 0); diff --git a/itest/rust/src/register_tests/gdscript_ffi_test.rs b/itest/rust/src/register_tests/gdscript_ffi_test.rs index 3052e02b7..5d88c0955 100644 --- a/itest/rust/src/register_tests/gdscript_ffi_test.rs +++ b/itest/rust/src/register_tests/gdscript_ffi_test.rs @@ -7,8 +7,10 @@ #![allow(dead_code)] +// While some of these lints are legitimate, no one cares for this generated code, and it's definitely not worth complicating the generator. #[rustfmt::skip] -#[allow(clippy::partialeq_to_none)] +#[allow(clippy::partialeq_to_none)] // i == None -> i.is_none() +#[allow(clippy::cmp_owned)] // i == GString::from("hello") -> i == "hello" pub mod gen_ffi { include!(concat!(env!("OUT_DIR"), "/gen_ffi.rs")); }