1use crate::{Any, Bytes, ContextError, Iterator, Module, Protocol, Value, VmError, VmErrorKind};
4
5pub fn module() -> Result<Module, ContextError> {
7 let mut module = Module::with_crate_item("std", &["string"]);
8
9 module.ty::<String>()?;
10
11 module.function(&["String", "from_str"], <String as From<&str>>::from)?;
12 module.function(&["String", "new"], String::new)?;
13 module.function(&["String", "with_capacity"], String::with_capacity)?;
14
15 module.inst_fn("cmp", str::cmp)?;
16 module.inst_fn("len", String::len)?;
17 module.inst_fn("starts_with", str::starts_with::<&str>)?;
18 module.inst_fn("ends_with", str::ends_with::<&str>)?;
19 module.inst_fn("capacity", String::capacity)?;
20 module.inst_fn("clear", String::clear)?;
21 module.inst_fn("push", String::push)?;
22 module.inst_fn("push_str", String::push_str)?;
23 module.inst_fn("reserve", String::reserve)?;
24 module.inst_fn("reserve_exact", String::reserve_exact)?;
25 module.inst_fn("into_bytes", into_bytes)?;
26 module.inst_fn("clone", String::clone)?;
27 module.inst_fn("shrink_to_fit", String::shrink_to_fit)?;
28 module.inst_fn("char_at", char_at)?;
29 module.inst_fn("split", string_split)?;
30 module.inst_fn("trim", string_trim)?;
31 module.inst_fn("trim_end", string_trim_end)?;
32 module.inst_fn("replace", str::replace::<&str>)?;
33 module.inst_fn("split_str", string_split)?;
35 module.inst_fn("is_empty", str::is_empty)?;
36 module.inst_fn("chars", string_chars)?;
37 module.inst_fn(Protocol::ADD, add)?;
38 module.inst_fn(Protocol::ADD_ASSIGN, String::push_str)?;
39 module.inst_fn(Protocol::INDEX_GET, string_index_get)?;
40 module.inst_fn("get", string_get)?;
41
42 module.function(&["parse_int"], parse_int)?;
44 module.function(&["parse_char"], parse_char)?;
45
46 Ok(module)
47}
48
49#[derive(Any, Debug, Clone, Copy)]
50#[rune(module = "crate", install_with = "NotCharBoundary::install")]
51struct NotCharBoundary(());
52
53impl NotCharBoundary {
54 fn string_display(&self, s: &mut String) -> std::fmt::Result {
55 use std::fmt::Write as _;
56 write!(s, "index outside of character boundary")
57 }
58
59 fn install(m: &mut Module) -> Result<(), ContextError> {
60 m.inst_fn(crate::Protocol::STRING_DISPLAY, Self::string_display)?;
61 Ok(())
62 }
63}
64
65fn into_bytes(s: String) -> Bytes {
67 Bytes::from_vec(s.into_bytes())
68}
69
70fn char_at(s: &str, index: usize) -> Option<char> {
71 if !s.is_char_boundary(index) {
72 return None;
73 }
74
75 s[index..].chars().next()
76}
77
78fn string_split(this: &str, value: Value) -> Result<Iterator, VmError> {
79 let lines = match value {
80 Value::String(s) => this
81 .split(s.borrow_ref()?.as_str())
82 .map(String::from)
83 .collect::<Vec<String>>(),
84 Value::StaticString(s) => this
85 .split(s.as_str())
86 .map(String::from)
87 .collect::<Vec<String>>(),
88 Value::Char(pat) => this.split(pat).map(String::from).collect::<Vec<String>>(),
89 value => return Err(VmError::bad_argument::<String>(0, &value)?),
90 };
91
92 Ok(Iterator::from_double_ended(
93 "std::str::Split",
94 lines.into_iter(),
95 ))
96}
97
98fn string_trim(this: &str) -> String {
99 this.trim().to_owned()
100}
101
102fn string_trim_end(this: &str) -> String {
103 this.trim_end().to_owned()
104}
105
106fn parse_int(s: &str) -> Result<i64, std::num::ParseIntError> {
107 str::parse::<i64>(s)
108}
109
110fn parse_char(s: &str) -> Result<char, std::char::ParseCharError> {
111 str::parse::<char>(s)
112}
113
114fn add(a: &str, b: &str) -> String {
116 let mut string = String::with_capacity(a.len() + b.len());
117 string.push_str(a);
118 string.push_str(b);
119 string
120}
121
122fn string_chars(s: &str) -> Iterator {
123 let iter = s.chars().collect::<Vec<_>>().into_iter();
124 Iterator::from_double_ended("std::str::Chars", iter)
125}
126
127fn string_get(s: &str, key: Value) -> Result<Option<String>, VmError> {
129 use crate::{FromValue as _, RangeLimits, TypeOf as _};
130
131 match key {
132 Value::Range(range) => {
133 let range = range.borrow_ref()?;
134
135 let start = match range.start.clone() {
136 Some(value) => Some(<usize>::from_value(value)?),
137 None => None,
138 };
139
140 let end = match range.end.clone() {
141 Some(value) => Some(<usize>::from_value(value)?),
142 None => None,
143 };
144
145 let out = match range.limits {
146 RangeLimits::HalfOpen => match (start, end) {
147 (Some(start), Some(end)) => s.get(start..end),
148 (Some(start), None) => s.get(start..),
149 (None, Some(end)) => s.get(..end),
150 (None, None) => s.get(..),
151 },
152 RangeLimits::Closed => match (start, end) {
153 (Some(start), Some(end)) => s.get(start..=end),
154 (None, Some(end)) => s.get(..=end),
155 _ => return Err(VmError::from(VmErrorKind::UnsupportedRange)),
156 },
157 };
158
159 return Ok(match out {
160 Some(out) => Some(out.to_owned()),
161 None => None,
162 });
163 }
164 index => Err(VmError::from(VmErrorKind::UnsupportedIndexGet {
165 target: String::type_info(),
166 index: index.type_info()?,
167 })),
168 }
169}
170
171fn string_index_get(s: &str, key: Value) -> Result<String, VmError> {
173 string_get(s, key)?.ok_or_else(|| VmError::panic("missing string slice"))
174}