diff --git a/src/bbcode/mod.rs b/src/bbcode/mod.rs index e6db295..9af5b48 100644 --- a/src/bbcode/mod.rs +++ b/src/bbcode/mod.rs @@ -1,14 +1,14 @@ -use std::{collections::HashMap, fmt::Display, sync::Arc}; +use std::{borrow::Cow, collections::HashMap, fmt::Display, sync::Arc}; pub mod parser; #[derive(Debug, Clone, PartialEq, Eq)] -pub enum BbcodeNode { - Tag(BbcodeTag), - Text(String), +pub enum BbcodeNode<'a> { + Tag(BbcodeTag<'a>), + Text(Cow<'a, str>), } -impl Display for BbcodeNode { +impl Display for BbcodeNode<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BbcodeNode::Tag(node) => node.fmt(f), @@ -18,25 +18,25 @@ impl Display for BbcodeNode { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BbcodeTag { +pub struct BbcodeTag<'a> { /// The name of the tag, e.g. `tag` for `[tag]something[/tag]`. - name: String, + name: &'a str, /// A simple parameter for the tag, e.g. `value` for `[tag=value]something[/tag]`. - simple_param: Option, + simple_param: Option>, /// Complex parameters, e.g. the map `value1` -> `xxx`, `value2` -> `yyy` for `[tag value1=”xxx” value2=”yyy”]something[/tag]`. - complex_params: HashMap, + complex_params: HashMap<&'a str, Cow<'a, str>>, /// The child nodes (or text) contained inside this node. - children: Vec>, + children: Vec>>, } -impl BbcodeTag { +impl<'a> BbcodeTag<'a> { /// Create a new, empty tag. - pub fn new>(name: S) -> Self { + pub fn new(name: &'a str) -> Self { Self { - name: name.into(), + name, simple_param: None, complex_params: HashMap::new(), children: Vec::new(), @@ -45,27 +45,27 @@ impl BbcodeTag { /// Add a simple parameter to the tag. #[cfg(test)] - pub fn with_simple_param>(mut self, tag_param: P) -> Self { + pub fn with_simple_param>>(mut self, tag_param: P) -> Self { self.simple_param = Some(tag_param.into()); self } /// Add a simple parameter to the tag. - pub fn add_simple_param>(&mut self, tag_param: P) -> &mut Self { + pub fn add_simple_param>>(&mut self, tag_param: P) -> &mut Self { self.simple_param = Some(tag_param.into()); self } /// Add a key/value parameter. #[cfg(test)] - pub fn with_param, V: Into>(mut self, key: K, value: V) -> Self { + pub fn with_param, V: Into>>(mut self, key: K, value: V) -> Self { self.complex_params.insert(key.into(), value.into()); self } /// Add a nested tag inside this one. #[cfg(test)] - pub fn with_tag(mut self, tag: BbcodeTag) -> Self { + pub fn with_tag(mut self, tag: BbcodeTag<'a>) -> Self { self.children.push(Arc::new(BbcodeNode::Tag(tag))); self } @@ -73,27 +73,28 @@ impl BbcodeTag { /// Add text inside of the node. #[cfg(test)] pub fn with_text>(mut self, text: T) -> Self { - self.children.push(Arc::new(BbcodeNode::Text(text.into()))); + self.children + .push(Arc::new(BbcodeNode::Text(Cow::Owned(text.into())))); self } /// The name of this tag. pub fn name(&self) -> &str { - &self.name + self.name } /// The child nodes of this tag. - pub fn children(&self) -> &Vec> { + pub fn children(&self) -> &Vec>> { &self.children } /// If it exists, the simple tag parameter of this tag. - pub fn simple_param(&self) -> &Option { + pub fn simple_param(&self) -> &Option> { &self.simple_param } } -impl Display for BbcodeTag { +impl Display for BbcodeTag<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_param(param: &str) -> String { if param.contains(' ') { diff --git a/src/bbcode/parser.rs b/src/bbcode/parser.rs index 88a17e0..76bafb9 100644 --- a/src/bbcode/parser.rs +++ b/src/bbcode/parser.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{borrow::Cow, sync::Arc}; use nom::{ branch::alt, @@ -25,28 +25,30 @@ pub fn parse_bbcode(input: &str) -> IResult<&str, Vec>> { fn parse_bbcode_internal<'a, E: ParseError<&'a str>>( input: &'a str, -) -> IResult<&'a str, Vec>, E> { +) -> IResult<&'a str, Vec>>, E> { many0(map(parse_node, |element| element.into()))(input) } -fn parse_node<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, BbcodeNode, E> { +fn parse_node<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, BbcodeNode<'a>, E> { alt(( map(parse_text, BbcodeNode::Text), map(parse_tag, BbcodeNode::Tag), ))(input) } -fn parse_tag<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, BbcodeTag, E> { +fn parse_tag<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, BbcodeTag<'a>, E> { let (input, mut tag) = parse_opening_tag(input)?; let (input, children) = parse_bbcode_internal(input)?; - let (input, _) = parse_closing_tag(input, &tag.name)?; + let (input, _) = parse_closing_tag(input, tag.name)?; tag.children = children; Ok((input, tag)) } -fn parse_opening_tag<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, BbcodeTag, E> { +fn parse_opening_tag<'a, E: ParseError<&'a str>>( + input: &'a str, +) -> IResult<&'a str, BbcodeTag<'a>, E> { let (mut input, mut tag) = map(preceded(char('['), alpha1), BbcodeTag::new)(input)?; if let Ok((new_input, simple_param)) = preceded(char('='), parse_param::)(input) { @@ -69,19 +71,21 @@ fn parse_closing_tag<'a, E: ParseError<&'a str>>( )(input) } -fn parse_text<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, String, E> { +fn parse_text<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Cow<'a, str>, E> { parse_inner_string("[]\\").parse(input) } -fn parse_param<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, String, E> { +fn parse_param<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, Cow<'a, str>, E> { alt(( parse_quoted_string, - map(parse_literal("\"\\[]"), |literal| literal.to_string()), + map(parse_literal("\"\\[]"), Cow::Borrowed), )) .parse(input) } -fn parse_quoted_string<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, String, E> { +fn parse_quoted_string<'a, E: ParseError<&'a str>>( + input: &'a str, +) -> IResult<&'a str, Cow<'a, str>, E> { delimited( char('"'), map(opt(parse_inner_string("\"\\")), |string| { @@ -94,17 +98,25 @@ fn parse_quoted_string<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&' fn parse_inner_string<'a, E: ParseError<&'a str>>( exclude: &'a str, -) -> impl Parser<&'a str, String, E> { +) -> impl Parser<&'a str, Cow<'a, str>, E> { move |input| { fold_many1( parse_fragment(exclude), - String::new, - |mut string, fragment| { + Cow::<'a, str>::default, + |mut cow, fragment| { match fragment { - StringFragment::Literal(s) => string.push_str(s), - StringFragment::EscapedChar(c) => string.push(c), + StringFragment::Literal(s) => { + if cow.is_empty() { + cow = Cow::Borrowed(s); + } else { + cow.to_mut().push_str(s); + } + } + StringFragment::EscapedChar(c) => { + cow.to_mut().push(c); + } } - string + cow }, ) .parse(input) diff --git a/src/bevy/bbcode.rs b/src/bevy/bbcode.rs index e575699..e823bce 100644 --- a/src/bevy/bbcode.rs +++ b/src/bevy/bbcode.rs @@ -6,7 +6,6 @@ use super::color::BbCodeColor; #[derive(Debug, Clone, Component, Default)] #[require(Text, BbcodeSettings)] - pub struct Bbcode { /// The bbcode-formatted text. pub content: String, diff --git a/src/bevy/conversion.rs b/src/bevy/conversion.rs index d69ce91..e8750a7 100644 --- a/src/bevy/conversion.rs +++ b/src/bevy/conversion.rs @@ -48,7 +48,7 @@ impl BbcodeContext { } } else { Self { - color: color.clone().into(), + color: color.to_string().into(), ..self.clone() } } @@ -60,7 +60,7 @@ impl BbcodeContext { "m" | "marker" => { if let Some(marker) = tag.simple_param() { let mut markers = self.markers.clone(); - markers.push(marker.clone()); + markers.push(marker.to_string()); Self { markers, @@ -74,7 +74,7 @@ impl BbcodeContext { "font" => { if let Some(font_family) = tag.simple_param() { Self { - font_family: font_family.clone(), + font_family: font_family.to_string(), ..self.clone() } } else {