godot_core/builtin/string/
mod.rs1mod gstring;
11mod macros;
12mod node_path;
13mod string_macros;
14mod string_name;
15
16use crate::meta::error::ConvertError;
17use crate::meta::{FromGodot, GodotConvert, ToGodot};
18use std::ops;
19
20pub use gstring::*;
21pub use node_path::NodePath;
22pub use string_name::*;
23
24impl GodotConvert for &str {
25 type Via = GString;
26}
27
28impl ToGodot for &str {
29 type ToVia<'v>
30 = GString
31 where
32 Self: 'v;
33
34 fn to_godot(&self) -> Self::ToVia<'_> {
35 GString::from(*self)
36 }
37}
38
39impl GodotConvert for String {
40 type Via = GString;
41}
42
43impl ToGodot for String {
44 type ToVia<'v> = Self::Via;
45
46 fn to_godot(&self) -> Self::ToVia<'_> {
47 GString::from(self)
48 }
49}
50
51impl FromGodot for String {
52 fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
53 Ok(via.to_string())
54 }
55}
56
57#[non_exhaustive]
64#[derive(Copy, Clone, Eq, PartialEq, Debug)]
65pub enum Encoding {
66 Ascii,
67 Latin1,
68 Utf8,
69}
70
71fn to_godot_fromlen_neg1<R>(range: R) -> (i64, i64)
77where
78 R: ops::RangeBounds<usize>,
79{
80 let from = match range.start_bound() {
81 ops::Bound::Included(&n) => n as i64,
82 ops::Bound::Excluded(&n) => (n as i64) + 1,
83 ops::Bound::Unbounded => 0,
84 };
85
86 let len = match range.end_bound() {
87 ops::Bound::Included(&n) => {
88 let to = (n + 1) as i64;
89 debug_assert!(
90 from <= to,
91 "range: start ({from}) > inclusive end ({n}) + 1"
92 );
93 to - from
94 }
95 ops::Bound::Excluded(&n) => {
96 let to = n as i64;
97 debug_assert!(from <= to, "range: start ({from}) > exclusive end ({to})");
98 to - from
99 }
100 ops::Bound::Unbounded => -1,
101 };
102
103 (from, len)
104}
105
106fn to_godot_fromlen_i32max<R>(range: R) -> (i64, i64)
111where
112 R: ops::RangeBounds<usize>,
113{
114 let (from, len) = to_godot_fromlen_neg1(range);
115 if len == -1 {
116 (from, i32::MAX as i64)
118 } else {
119 (from, len)
120 }
121}
122
123fn to_godot_fromto<R>(range: R) -> (i64, i64)
127where
128 R: ops::RangeBounds<usize>,
129{
130 let (from, len) = to_godot_fromlen_neg1(range);
131 if len == -1 {
132 (from, 0)
133 } else {
134 (from, from + len)
135 }
136}
137
138fn populated_or_none(s: GString) -> Option<GString> {
139 if s.is_empty() {
140 None
141 } else {
142 Some(s)
143 }
144}
145
146fn found_to_option(index: i64) -> Option<usize> {
147 if index == -1 {
148 None
149 } else {
150 let index_usize = index
152 .try_into()
153 .unwrap_or_else(|_| panic!("unexpected index {index} returned from Godot function"));
154
155 Some(index_usize)
156 }
157}
158
159use standard_fmt::pad_if_needed;
164
165mod standard_fmt {
166 use std::fmt;
167 use std::fmt::Write;
168
169 pub fn pad_if_needed<F>(f: &mut fmt::Formatter<'_>, display_impl: F) -> fmt::Result
170 where
171 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
172 {
173 let needs_format = f.width().is_some() || f.precision().is_some() || f.align().is_some();
174
175 if !needs_format {
177 return display_impl(f);
178 }
179
180 let ic = FmtInterceptor { display_impl };
181
182 let mut local_str = String::new();
183 write!(&mut local_str, "{ic}")?;
184 f.pad(&local_str)
185 }
186
187 struct FmtInterceptor<F>
188 where
189 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
190 {
191 display_impl: F,
192 }
193
194 impl<F> fmt::Display for FmtInterceptor<F>
195 where
196 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
197 {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 (self.display_impl)(f)
200 }
201 }
202}