diff --git a/examples/hot_reload.rs b/examples/hot_reload.rs index 32de0401b..a0b296502 100644 --- a/examples/hot_reload.rs +++ b/examples/hot_reload.rs @@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element { }, p { - "High-Five counter: {count}", + "High-Five counter: {count.to_string():?}", } div { @@ -43,33 +43,31 @@ fn app(cx: Scope) -> Element { cx.render(rsx! { div { - display: "flex", - flex_direction: "row", - width: "100%", - height: "50%", - Editable{ - current_code: submitted_rsx_code.get().clone(), - }, + display: "flex", + flex_direction: "row", + width: "100%", + height: "50%", + Editable{ + current_code: submitted_rsx_code.get().clone(), + }, - textarea { - width: "90%", - value: - rsx_code - , - oninput: move |evt| { - rsx_code.set(evt.value.clone()); - }, - } + textarea { + width: "90%", + value: rsx_code, + oninput: move |evt| { + rsx_code.set(evt.value.clone()); + }, + } - button { - height: "100%", - width: "10%", - onclick: move |_|{ - submitted_rsx_code.set(Some(rsx_code.get().clone())); - }, - "submit" - } - } + button { + height: "100%", + width: "10%", + onclick: move |_|{ + submitted_rsx_code.set(Some(rsx_code.get().clone())); + }, + "submit" + } + } }) } @@ -86,7 +84,7 @@ fn Editable(cx: Scope) -> Element { rsx_index.insert( CodeLocation { file: r"examples\hot_reload.rs".to_string(), - line: 95, + line: 93, column: 15, }, code.clone(), @@ -101,7 +99,7 @@ fn Editable(cx: Scope) -> Element { }, p { - "High-Five counter: {count}", + "High-Five counter: {count.to_string():?}", } div { diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 0827f6abe..d97b381c9 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -188,40 +188,44 @@ pub fn rsx(s: TokenStream) -> TokenStream { { use dioxus_rsx_interperter::captuered_context::CapturedContextBuilder; - let captured = CapturedContextBuilder::from_call_body(body); - quote::quote! { - { - let __line_num = get_line_num(); - let __rsx_text_index: RsxTextIndex = cx.consume_context().unwrap(); - // only the insert the rsx text once - if !__rsx_text_index.read().contains_key(&__line_num){ - __rsx_text_index.insert( - __line_num.clone(), - #rsx_text.to_string(), - ); - } - LazyNodes::new(move |__cx|{ - if let Some(__text) = { - let read = __rsx_text_index.read(); - // clone prevents deadlock on nested rsx calls - read.get(&__line_num).cloned() - } { - match interpert_rsx( - __cx, - &__text, - #captured - ){ - Ok(vnode) => vnode, - Err(err) => __cx.text(format_args!("{:?}", err)) + match CapturedContextBuilder::from_call_body(body) { + Ok(captured) => { + quote::quote! { + { + let __line_num = get_line_num(); + let __rsx_text_index: RsxTextIndex = cx.consume_context().unwrap(); + // only the insert the rsx text once + if !__rsx_text_index.read().contains_key(&__line_num){ + __rsx_text_index.insert( + __line_num.clone(), + #rsx_text.to_string(), + ); } + LazyNodes::new(move |__cx|{ + if let Some(__text) = { + let read = __rsx_text_index.read(); + // clone prevents deadlock on nested rsx calls + read.get(&__line_num).cloned() + } { + match interpert_rsx( + __cx, + &__text, + #captured + ){ + Ok(vnode) => vnode, + Err(err) => __cx.text(format_args!("{:?}", err)) + } + } + else { + panic!("rsx: line number {:?} not found in rsx index", __line_num); + } + }) } - else { - panic!("rsx: line number {:?} not found in rsx index", __line_num); - } - }) + } + .into() } + Err(err) => err.into_compile_error().into(), } - .into() } #[cfg(not(feature = "hot_reload"))] body.to_token_stream().into() diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index 23f83be1b..24ef50c1d 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -9,8 +9,4 @@ license = "MIT/Apache-2.0" [dependencies] proc-macro2 = { version = "1.0" } syn = { version = "1.0", features = ["full", "extra-traits"] } -quote = { version = "1.0", optional = true } - -[features] -default = ["to_tokens"] -to_tokens = ["quote"] \ No newline at end of file +quote = { version = "1.0" } diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index 3fe690bb3..9540b0b15 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -14,7 +14,6 @@ use super::*; use proc_macro2::TokenStream as TokenStream2; -#[cfg(feature = "to_tokens")] use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ ext::IdentExt, @@ -73,7 +72,6 @@ impl Parse for Component { } } -#[cfg(feature = "to_tokens")] impl ToTokens for Component { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 0036e7bff..38ab75575 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -1,7 +1,6 @@ use super::*; use proc_macro2::TokenStream as TokenStream2; -#[cfg(feature = "to_tokens")] use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, @@ -159,7 +158,6 @@ impl Parse for Element { } } -#[cfg(feature = "to_tokens")] impl ToTokens for Element { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; @@ -218,7 +216,6 @@ pub struct ElementAttrNamed { pub attr: ElementAttr, } -#[cfg(feature = "to_tokens")] impl ToTokens for ElementAttrNamed { fn to_tokens(&self, tokens: &mut TokenStream2) { let ElementAttrNamed { el_name, attr } = self; diff --git a/packages/rsx/src/ifmt.rs b/packages/rsx/src/ifmt.rs index 7b7b12e40..fba87c2f9 100644 --- a/packages/rsx/src/ifmt.rs +++ b/packages/rsx/src/ifmt.rs @@ -1,24 +1,60 @@ -#[cfg(feature = "to_tokens")] -use ::quote::{quote, ToTokens}; -use ::std::ops::Not; -use ::syn::{ +use proc_macro2::{Span, TokenStream}; + +use quote::{quote, ToTokens}; +use syn::{ parse::{Parse, ParseStream}, - punctuated::Punctuated, *, }; -use proc_macro2::TokenStream; -#[cfg(feature = "to_tokens")] pub fn format_args_f_impl(input: IfmtInput) -> Result { - let IfmtInput { - format_literal, - positional_args, - named_args, - } = input; + // build format_literal + let mut format_literal = String::new(); + let mut expr_counter = 0; + for segment in input.segments.iter() { + match segment { + Segment::Literal(s) => format_literal += &s, + Segment::Formatted { + format_args, + segment, + } => { + format_literal += "{"; + match segment { + FormattedSegment::Expr(_) => { + format_literal += &expr_counter.to_string(); + expr_counter += 1; + } + FormattedSegment::Ident(ident) => { + format_literal += &ident.to_string(); + } + } + format_literal += ":"; + format_literal += format_args; + format_literal += "}"; + } + } + } - let named_args = named_args.into_iter().map(|(ident, expr)| { - quote! { - #ident = #expr + let positional_args = input.segments.iter().filter_map(|seg| { + if let Segment::Formatted { segment, .. } = seg { + if let FormattedSegment::Expr(expr) = segment { + Some(expr) + } else { + None + } + } else { + None + } + }); + + let named_args = input.segments.iter().filter_map(|seg| { + if let Segment::Formatted { segment, .. } = seg { + if let FormattedSegment::Ident(ident) = segment { + Some(quote! {#ident = #ident}) + } else { + None + } + } else { + None } }); @@ -34,219 +70,102 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result { #[allow(dead_code)] // dumb compiler does not see the struct being used... #[derive(Debug)] pub struct IfmtInput { - pub format_literal: LitStr, - pub positional_args: Vec, - pub named_args: Vec<(Ident, Expr)>, + pub segments: Vec, } impl IfmtInput { - fn parse_segments(self) -> Result { - let IfmtInput { - mut format_literal, - mut positional_args, - mut named_args, - } = self; - - let s = format_literal.value(); - let out_format_literal = &mut String::with_capacity(s.len()); - - let mut iterator = s.char_indices().peekable(); - while let Some((i, c)) = iterator.next() { - out_format_literal.push(c); - if c != '{' { - continue; - } - // encountered `{`, let's see if it was `{{` - if let Some(&(_, '{')) = iterator.peek() { - let _ = iterator.next(); - out_format_literal.push('{'); - continue; - } - let (end, colon_or_closing_brace) = iterator - .find(|&(_, c)| c == '}' || c == ':') - .expect(concat!( - "Invalid format string literal\n", - "note: if you intended to print `{`, ", - "you can escape it using `{{`", - )); - // We use defer to ensure all the `continue`s append the closing char. - let mut out_format_literal = defer(&mut *out_format_literal, |it| { - it.push(colon_or_closing_brace) - }); - let out_format_literal: &mut String = *out_format_literal; - let mut arg = s[i + 1..end].trim(); - if let Some("=") = arg.get(arg.len().saturating_sub(1)..) { - assert_eq!( - out_format_literal.pop(), // Remove the opening brace - Some('{'), - ); - arg = &arg[..arg.len() - 1]; - out_format_literal.push_str(arg); - out_format_literal.push_str(" = {"); - } - if arg.is_empty() { - continue; - } - - #[derive(Debug)] - enum Segment { - Ident(Ident), - LitInt(LitInt), - } - let segments: Vec = { - impl Parse for Segment { - fn parse(input: ParseStream<'_>) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(Ident) { - input.parse().map(Segment::Ident) - } else if lookahead.peek(LitInt) { - input.parse().map(Segment::LitInt) - } else { - Err(lookahead.error()) - } - } + pub fn from_str(input: &str) -> Result { + let mut chars = input.chars().peekable(); + let mut segments = Vec::new(); + let mut current_literal = String::new(); + while let Some(c) = chars.next() { + if c == '{' { + if let Some(c) = chars.next_if(|c| *c == '{') { + current_literal.push(c); + continue; } - match ::syn::parse::Parser::parse_str( - Punctuated::::parse_separated_nonempty, - arg, - ) { - Ok(segments) => segments.into_iter().collect(), - Err(err) => return Err(err), - } - }; - match segments.len() { - 0 => unreachable!("`parse_separated_nonempty` returned empty"), - 1 => { - out_format_literal.push_str(arg); - match { segments }.pop().unwrap() { - Segment::LitInt(_) => { - // found something like `{0}`, let `format_args!` - // handle it. - continue; - } - Segment::Ident(ident) => { - // if `ident = ...` is not yet among the extra args - if named_args.iter().all(|(it, _)| *it != ident) { - named_args.push(( - ident.clone(), - parse_quote!(#ident), // Expr - )); + segments.push(Segment::Literal(current_literal)); + current_literal = String::new(); + let mut current_captured = String::new(); + while let Some(c) = chars.next() { + if c == ':' { + let mut current_format_args = String::new(); + while let Some(c) = chars.next() { + if c == '}' { + segments.push(Segment::Formatted { + format_args: current_format_args, + segment: FormattedSegment::parse(¤t_captured)?, + }); + break; } + current_format_args.push(c); } + break; } + if c == '}' { + segments.push(Segment::Formatted { + format_args: String::new(), + segment: FormattedSegment::parse(¤t_captured)?, + }); + break; + } + current_captured.push(c); } - _ => { - ::std::fmt::Write::write_fmt( - out_format_literal, - format_args!("{}", positional_args.len()), - ) - .expect("`usize` or `char` Display impl cannot panic"); - let segments: Punctuated = segments - .into_iter() - .map(|it| match it { - Segment::Ident(ident) => ident.into_token_stream(), - Segment::LitInt(literal) => literal.into_token_stream(), - }) - .collect(); - positional_args.push(parse_quote! { - #segments - }) - } + } else { + current_literal.push(c); } } - format_literal = LitStr::new(out_format_literal, format_literal.span()); - - Ok(Self { - format_literal, - positional_args, - named_args, - }) + segments.push(Segment::Literal(current_literal)); + Ok(Self { segments }) } +} - fn parse_positional_args(input: ParseStream) -> Result { - let format_literal = input.parse()?; - let mut positional_args = vec![]; - loop { - if input.parse::>()?.is_none() { - return Ok(Self { - format_literal, - positional_args, - named_args: vec![], - }); +#[derive(Debug)] +pub enum Segment { + Literal(String), + Formatted { + format_args: String, + segment: FormattedSegment, + }, +} + +#[derive(Debug)] +pub enum FormattedSegment { + Expr(Expr), + Ident(Ident), +} + +impl FormattedSegment { + fn parse(input: &str) -> Result { + if let Ok(ident) = parse_str::(input) { + if &ident.to_string() == input { + return Ok(Self::Ident(ident)); } - if input.peek(Ident) && input.peek2(Token![=]) && input.peek3(Token![=]).not() { - // Found a positional parameter - break; - } - positional_args.push(input.parse()?); } - let named_args = Punctuated::<_, Token![,]>::parse_terminated_with(input, |input| { - Ok({ - let name: Ident = input.parse()?; - let _: Token![=] = input.parse()?; - let expr: Expr = input.parse()?; - (name, expr) - }) - })? - .into_iter() - .collect(); + if let Ok(expr) = parse_str(&("{".to_string() + input + "}")) { + Ok(Self::Expr(expr)) + } else { + Err(Error::new( + Span::call_site(), + "Expected Ident or Expression", + )) + } + } +} - Ok(Self { - format_literal, - positional_args, - named_args, - }) +impl ToTokens for FormattedSegment { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Expr(expr) => expr.to_tokens(tokens), + Self::Ident(ident) => ident.to_tokens(tokens), + } } } impl Parse for IfmtInput { fn parse(input: ParseStream) -> Result { - Self::parse_positional_args(input).and_then(|new| new.parse_segments()) + let input: LitStr = input.parse()?; + let input_str = input.value(); + IfmtInput::from_str(&input_str) } } - -pub fn defer<'a, T: 'a, Drop: 'a>(x: T, drop: Drop) -> impl ::core::ops::DerefMut + 'a -where - Drop: FnOnce(T), -{ - use ::core::mem::ManuallyDrop; - struct Ret(ManuallyDrop, ManuallyDrop) - where - Drop: FnOnce(T); - impl ::core::ops::Drop for Ret - where - Drop: FnOnce(T), - { - fn drop(&'_ mut self) { - use ::core::ptr; - unsafe { - // # Safety - // - // - This is the canonical example of using `ManuallyDrop`. - let value = ManuallyDrop::into_inner(ptr::read(&self.0)); - let drop = ManuallyDrop::into_inner(ptr::read(&self.1)); - drop(value); - } - } - } - impl ::core::ops::Deref for Ret - where - Drop: FnOnce(T), - { - type Target = T; - #[inline] - fn deref(&'_ self) -> &'_ Self::Target { - &self.0 - } - } - impl ::core::ops::DerefMut for Ret - where - Drop: FnOnce(T), - { - #[inline] - fn deref_mut(&'_ mut self) -> &'_ mut Self::Target { - &mut self.0 - } - } - Ret(ManuallyDrop::new(x), ManuallyDrop::new(drop)) -} diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index 0f9025e5e..1881f67e4 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -1,7 +1,6 @@ use super::*; use proc_macro2::TokenStream as TokenStream2; -#[cfg(feature = "to_tokens")] use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, @@ -79,7 +78,6 @@ impl Parse for BodyNode { } } -#[cfg(feature = "to_tokens")] impl ToTokens for BodyNode { fn to_tokens(&self, tokens: &mut TokenStream2) { match &self { diff --git a/packages/rsx_interperter/src/captuered_context.rs b/packages/rsx_interperter/src/captuered_context.rs index fc9f41ae3..530993532 100644 --- a/packages/rsx_interperter/src/captuered_context.rs +++ b/packages/rsx_interperter/src/captuered_context.rs @@ -1,8 +1,10 @@ use dioxus_core::{Listener, VNode}; -use dioxus_rsx::{BodyNode, CallBody, Component, ElementAttr, ElementAttrNamed, IfmtInput}; +use dioxus_rsx::{ + BodyNode, CallBody, Component, ElementAttr, ElementAttrNamed, IfmtInput, Segment, +}; use quote::{quote, ToTokens, TokenStreamExt}; use std::collections::HashMap; -use syn::Expr; +use syn::{Expr, Result}; #[derive(Default)] pub struct CapturedContextBuilder { @@ -24,15 +26,15 @@ impl CapturedContextBuilder { self.captured_expressions.extend(other.captured_expressions); } - pub fn from_call_body(body: CallBody) -> Self { + pub fn from_call_body(body: CallBody) -> Result { let mut new = Self::default(); for node in body.roots { - new.extend(Self::find_captured(node)); + new.extend(Self::find_captured(node)?); } - new + Ok(new) } - fn find_captured(node: BodyNode) -> Self { + fn find_captured(node: BodyNode) -> Result { let mut captured = CapturedContextBuilder::default(); match node { BodyNode::Element(el) => { @@ -40,7 +42,7 @@ impl CapturedContextBuilder { match attr.attr { ElementAttr::AttrText { name, value } => { let (name, value_tokens) = (name.to_string(), value.to_token_stream()); - let formated: IfmtInput = syn::parse2(value_tokens).unwrap(); + let formated: IfmtInput = syn::parse2(value_tokens)?; captured.attributes.insert(name, formated); } ElementAttr::AttrExpression { name: _, value } => { @@ -48,7 +50,7 @@ impl CapturedContextBuilder { } ElementAttr::CustomAttrText { name, value } => { let (name, value_tokens) = (name.value(), value.to_token_stream()); - let formated: IfmtInput = syn::parse2(value_tokens).unwrap(); + let formated: IfmtInput = syn::parse2(value_tokens)?; captured.attributes.insert(name, formated); } ElementAttr::CustomAttrExpression { name: _, value } => { @@ -58,7 +60,7 @@ impl CapturedContextBuilder { } } for child in el.children { - captured.extend(Self::find_captured(child)); + captured.extend(Self::find_captured(child)?); } } BodyNode::Component(comp) => { @@ -71,7 +73,7 @@ impl CapturedContextBuilder { } BodyNode::RawExpr(_) => captured.iterators.push(node), } - captured + Ok(captured) } } @@ -95,34 +97,41 @@ impl ToTokens for CapturedContextBuilder { BodyNode::RawExpr(expr) => expr.to_token_stream().to_string(), _ => unreachable!(), }); - let captured_named: Vec<_> = attributes - .iter() - .map(|(_, fmt)| fmt.named_args.iter()) - .chain(text.iter().map(|fmt| fmt.named_args.iter())) + let captured: Vec<_> = attributes + .values() + .chain(text.iter()) + .map(|input| input.segments.iter()) .flatten() + .filter_map(|seg| match seg { + Segment::Formatted { + format_args, + segment, + } => { + let expr = segment.to_token_stream(); + let as_string = expr.to_string(); + let format_expr = if format_args.is_empty() { + "{".to_string() + format_args + "}" + } else { + "{".to_string() + ":" + format_args + "}" + }; + Some(quote! { + FormattedArg{ + expr: #as_string, + format_args: #format_args, + result: format!(#format_expr, #expr) + } + }) + } + _ => None, + }) .collect(); - let captured_expr: Vec<_> = attributes - .iter() - .map(|(_, fmt)| fmt.positional_args.iter()) - .chain(text.iter().map(|fmt| fmt.positional_args.iter())) - .flatten() - .collect(); - let captured_names = captured_named.iter().map(|(n, _)| n.to_string()).chain( - captured_expr - .iter() - .map(|expr| expr.to_token_stream().to_string()), - ); - let captured_expr = captured_named - .iter() - .map(|(_, e)| e) - .chain(captured_expr.iter().map(|expr| *expr)); let captured_attr_expressions_text = captured_expressions .iter() .map(|e| format!("{}", e.to_token_stream())); tokens.append_all(quote! { CapturedContext { captured: IfmtArgs{ - named_args: vec![#((#captured_names, #captured_expr.to_string())),*] + named_args: vec![#(#captured),*] }, components: vec![#((#compontents_str, #components)),*], iterators: vec![#((#iterators_str, #iterators)),*], @@ -149,6 +158,12 @@ pub struct CapturedContext<'a> { } pub struct IfmtArgs { - // live reload only supports named arguments - pub named_args: Vec<(&'static str, String)>, + // map expressions to the value string they produced + pub named_args: Vec, +} + +pub struct FormattedArg { + pub expr: &'static str, + pub format_args: &'static str, + pub result: String, } diff --git a/packages/rsx_interperter/src/interperter.rs b/packages/rsx_interperter/src/interperter.rs index 0ddd3b887..ed375e999 100644 --- a/packages/rsx_interperter/src/interperter.rs +++ b/packages/rsx_interperter/src/interperter.rs @@ -1,7 +1,6 @@ use dioxus_core::{Attribute, AttributeValue, NodeFactory, VNode}; -use dioxus_rsx::{BodyNode, CallBody, ElementAttr}; +use dioxus_rsx::{BodyNode, CallBody, ElementAttr, IfmtInput, Segment}; use quote::ToTokens; -use std::str::FromStr; use syn::{parse2, parse_str, Expr}; use crate::attributes::attrbute_to_static_str; @@ -9,28 +8,31 @@ use crate::captuered_context::{CapturedContext, IfmtArgs}; use crate::elements::element_to_static_str; use crate::error::{Error, RecompileReason}; -#[derive(Debug)] -enum Segment { - Ident(String), - Literal(String), -} +struct InterpertedIfmt(IfmtInput); -struct InterperedIfmt { - segments: Vec, -} - -impl InterperedIfmt { +impl InterpertedIfmt { fn resolve(&self, captured: &IfmtArgs) -> String { let mut result = String::new(); - for seg in &self.segments { + for seg in &self.0.segments { match seg { - Segment::Ident(name) => { - let (_, value) = captured + Segment::Formatted { + segment, + format_args, + } => { + let expr = segment.to_token_stream(); + let expr_str = expr.to_string(); + let expr: Expr = parse2(expr).unwrap(); + let formatted = captured .named_args .iter() - .find(|(n, _)| *n == name) - .expect(format!("could not resolve {}", name).as_str()); - result.push_str(value); + .find(|fmted| { + parse_str::(fmted.expr).unwrap() == expr + && fmted.format_args == format_args + }) + .expect( + format!("could not resolve {{{}:{}}}", expr_str, format_args).as_str(), + ); + result.push_str(&formatted.result); } Segment::Literal(lit) => result.push_str(lit), } @@ -39,52 +41,6 @@ impl InterperedIfmt { } } -impl FromStr for InterperedIfmt { - type Err = (); - fn from_str(input: &str) -> Result { - let mut segments = Vec::new(); - let mut segment = String::new(); - let mut chars = input.chars().peekable(); - while let Some(c) = chars.next() { - if c == '{' { - if chars.peek().copied() != Some('{') { - let old; - (old, segment) = (segment, String::new()); - if !old.is_empty() { - segments.push(Segment::Literal(old)); - } - while let Some(c) = chars.next() { - if c == '}' { - let old; - (old, segment) = (segment, String::new()); - if !old.is_empty() { - segments.push(Segment::Ident(old)); - } - break; - } - if c == ':' { - while Some('}') != chars.next() {} - let old; - (old, segment) = (segment, String::new()); - if !old.is_empty() { - segments.push(Segment::Ident(old)); - } - break; - } - segment.push(c); - } - } - } else { - segment.push(c); - } - } - if !segment.is_empty() { - segments.push(Segment::Literal(segment)); - } - Ok(Self { segments }) - } -} - pub fn build<'a>( rsx: CallBody, mut ctx: CapturedContext<'a>, @@ -105,7 +61,9 @@ fn build_node<'a>( let bump = factory.bump(); match node { BodyNode::Text(text) => { - let ifmt: InterperedIfmt = text.value().parse().unwrap(); + let ifmt = InterpertedIfmt( + IfmtInput::from_str(&text.value()).map_err(|err| Error::ParseError(err))?, + ); let text = bump.alloc(ifmt.resolve(&ctx.captured)); Ok(factory.text(format_args!("{}", text))) } @@ -114,13 +72,21 @@ fn build_node<'a>( for attr in &el.attributes { match &attr.attr { ElementAttr::AttrText { .. } | ElementAttr::CustomAttrText { .. } => { - let (name, value): (String, InterperedIfmt) = match &attr.attr { - ElementAttr::AttrText { name, value } => { - (name.to_string(), value.value().parse().unwrap()) - } - ElementAttr::CustomAttrText { name, value } => { - (name.value(), value.value().parse().unwrap()) - } + let (name, value): (String, InterpertedIfmt) = match &attr.attr { + ElementAttr::AttrText { name, value } => ( + name.to_string(), + InterpertedIfmt( + IfmtInput::from_str(&value.value()) + .map_err(|err| Error::ParseError(err))?, + ), + ), + ElementAttr::CustomAttrText { name, value } => ( + name.value(), + InterpertedIfmt( + IfmtInput::from_str(&value.value()) + .map_err(|err| Error::ParseError(err))?, + ), + ), _ => unreachable!(), }; @@ -145,7 +111,6 @@ fn build_node<'a>( ElementAttr::CustomAttrExpression { name, value } => { (name.value(), value) } - _ => unreachable!(), }; if let Some((_, resulting_value)) = ctx @@ -219,7 +184,9 @@ fn build_node<'a>( None, )), Some(lit) => { - let ifmt: InterperedIfmt = lit.value().parse().unwrap(); + let ifmt: InterpertedIfmt = InterpertedIfmt( + parse_str(&lit.value()).map_err(|err| Error::ParseError(err))?, + ); let key = bump.alloc(ifmt.resolve(&ctx.captured)); Ok(factory.raw_element( diff --git a/src/lib.rs b/src/lib.rs index 246ec8e96..ba7249509 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ pub mod prelude { #[cfg(feature = "hot_reload")] pub use dioxus_rsx_interperter::{ - captuered_context::{CapturedContext, IfmtArgs}, + captuered_context::{CapturedContext, FormattedArg, IfmtArgs}, get_line_num, interpert_rsx, with_hot_reload, CodeLocation, RsxTextIndex, }; }