add node dependancy

This commit is contained in:
Evan Almloff committed 2022-04-16 12:27:52 -05:00
1 parent 0fb9aed273
commit 436c6a02f7
18 files changed
+1312 -575

No files matched your search

+448 -235
View File
@@ -1,38 +1,17 @@
extern crate proc_macro;
use std::collections::BTreeMap;
mod sorted_slice;
use dioxus_native_core::state::MemberId;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use quote::{quote, ToTokens, __private::Span};
use sorted_slice::StrSlice;
use syn::{
self, bracketed,
self,
parse::{Parse, ParseStream, Result},
parse_macro_input,
punctuated::Punctuated,
token::Paren,
Field, Ident, LitStr, Token, Type, TypeTuple,
parse_macro_input, parse_quote, Error, Field, Ident, Token, Type,
};
struct StrSlice {
map: BTreeMap<String, LitStr>,
}
impl Parse for StrSlice {
fn parse(input: ParseStream) -> Result<Self> {
let content;
bracketed!(content in input);
let mut map = BTreeMap::new();
while let Ok(s) = content.parse::<LitStr>() {
map.insert(s.value(), s);
#[allow(unused_must_use)]
{
content.parse::<Token![,]>();
}
}
Ok(StrSlice { map })
}
}
#[proc_macro]
pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
let slice: StrSlice = parse_macro_input!(input as StrSlice);
@@ -40,14 +19,13 @@ pub fn sorted_str_slice(input: TokenStream) -> TokenStream {
quote!([#(#strings, )*]).into()
}
#[derive(PartialEq)]
#[derive(PartialEq, Debug, Clone)]
enum DepKind {
NodeDepState,
ChildDepState,
ParentDepState,
}
// macro that streams data from the State for any attributes that end with _
#[proc_macro_derive(State, attributes(node_dep_state, child_dep_state, parent_dep_state))]
pub fn state_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
@@ -60,153 +38,149 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream {
syn::Data::Struct(data) => match &data.fields {
syn::Fields::Named(e) => &e.named,
syn::Fields::Unnamed(_) => todo!("unnamed fields"),
syn::Fields::Unit => todo!("unit fields"),
syn::Fields::Unit => todo!("unit structs"),
}
.iter()
.collect(),
_ => unimplemented!(),
};
let strct = Struct::parse(&fields);
let state_strct = StateStruct::parse(&fields, &strct);
let strct = Struct::new(type_name.clone(), &fields);
match StateStruct::parse(&fields, &strct) {
Ok(state_strct) => {
let node_dep_state_fields = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::NodeDepState)
.map(|f| f.reduce_self());
let child_dep_state_fields = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ChildDepState)
.map(|f| f.reduce_self());
let parent_dep_state_fields = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ParentDepState)
.map(|f| f.reduce_self());
let node_dep_state_fields = quote::__private::TokenStream::from_iter(
state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::NodeDepState)
.map(|f| {
let ty_id = &f.type_id();
let reduce = &f.reduce_self();
quote! {
else if ty == #ty_id {
#reduce
let node_iter = state_strct
.state_members
.iter()
.filter(|m| m.dep_kind == DepKind::NodeDepState);
let node_ids = node_iter.clone().map(|m| m.member_id.0);
let node_ids_clone = node_ids.clone();
let node_types = node_iter.map(|f| &f.mem.ty);
let child_iter = state_strct
.state_members
.iter()
.filter(|m| m.dep_kind == DepKind::ChildDepState);
let child_ids = child_iter.clone().map(|m| m.member_id.0);
let child_ids_clone = child_ids.clone();
let child_types = child_iter.map(|f| &f.mem.ty);
let parent_iter = state_strct
.state_members
.iter()
.filter(|m| m.dep_kind == DepKind::ParentDepState);
let parent_ids = parent_iter.clone().map(|m| m.member_id.0);
let parent_ids_clone = parent_ids.clone();
let parent_types = parent_iter.map(|f| &f.mem.ty);
let type_name_str = type_name.to_string();
let gen = quote! {
impl State for #type_name{
fn update_node_dep_state<'a>(
&'a mut self,
ty: dioxus_native_core::state::MemberId,
node: &'a dioxus_core::VNode<'a>,
vdom: &'a dioxus_core::VirtualDom,
ctx: &anymap::AnyMap,
) -> Option<dioxus_native_core::state::NodeStatesChanged>{
use dioxus_native_core::state::NodeDepState as _;
match ty.0{
#(
#node_ids => #node_dep_state_fields,
)*
_ => panic!("{:?} not in {}", ty, #type_name_str),
}
}
fn update_parent_dep_state<'a>(
&'a mut self,
ty: dioxus_native_core::state::MemberId,
node: &'a dioxus_core::VNode<'a>,
vdom: &'a dioxus_core::VirtualDom,
parent: Option<&Self>,
ctx: &anymap::AnyMap,
) -> Option<dioxus_native_core::state::ParentStatesChanged>{
use dioxus_native_core::state::ParentDepState as _;
match ty.0{
#(
#parent_ids => #parent_dep_state_fields,
)*
_ => panic!("{:?} not in {}", ty, #type_name_str),
}
}
fn update_child_dep_state<'a>(
&'a mut self,
ty: dioxus_native_core::state::MemberId,
node: &'a dioxus_core::VNode<'a>,
vdom: &'a dioxus_core::VirtualDom,
children: &[&Self],
ctx: &anymap::AnyMap,
) -> Option<dioxus_native_core::state::ChildStatesChanged>{
use dioxus_native_core::state::ChildDepState as _;
match ty.0{
#(
#child_ids => {#child_dep_state_fields},
)*
_ => panic!("{:?} not in {}", ty, #type_name_str),
}
}
fn child_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
let mut dep_types = Vec::new();
#(if #child_types::NODE_MASK.overlaps(mask) {
dep_types.push(dioxus_native_core::state::MemberId(#child_ids_clone));
})*
dep_types
}
fn parent_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
let mut dep_types = Vec::new();
#(if #parent_types::NODE_MASK.overlaps(mask) {
dep_types.push(dioxus_native_core::state::MemberId(#parent_ids_clone));
})*
dep_types
}
fn node_dep_types(&self, mask: &dioxus_native_core::node_ref::NodeMask) -> Vec<dioxus_native_core::state::MemberId>{
let mut dep_types = Vec::new();
#(if #node_types::NODE_MASK.overlaps(mask) {
dep_types.push(dioxus_native_core::state::MemberId(#node_ids_clone));
})*
dep_types
}
}
}),
);
let child_dep_state_fields = quote::__private::TokenStream::from_iter(
state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ChildDepState)
.map(|f| {
let ty_id = &f.type_id();
let reduce = &f.reduce_self();
quote! {
else if ty == #ty_id {
#reduce
}
}
}),
);
let parent_dep_state_fields = quote::__private::TokenStream::from_iter(
state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ParentDepState)
.map(|f| {
let ty_id = &f.type_id();
let reduce = &f.reduce_self();
quote! {
else if ty == #ty_id {
#reduce
}
}
}),
);
let node_types = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::NodeDepState)
.map(|f| &f.mem.ty);
let child_types = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ChildDepState)
.map(|f| &f.mem.ty);
let parent_types = state_strct
.state_members
.iter()
.filter(|f| f.dep_kind == DepKind::ParentDepState)
.map(|f| &f.mem.ty);
let type_name_str = type_name.to_string();
let gen = quote! {
impl State for #type_name{
fn update_node_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, ctx: &anymap::AnyMap) -> bool{
use dioxus_native_core::state::NodeDepState as _;
// println!("called update_node_dep_state with ty: {:?}", ty);
if false {
unreachable!();
}
#node_dep_state_fields
else{
panic!("{:?} not in {}", ty, #type_name_str)
}
}
fn update_parent_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, parent: Option<&Self>, ctx: &anymap::AnyMap) -> bool{
use dioxus_native_core::state::ParentDepState as _;
// println!("called update_parent_dep_state with ty: {:?}", ty);
if false {
unreachable!();
}
#parent_dep_state_fields
else{
panic!("{:?} not in {}", ty, #type_name_str)
}
}
fn update_child_dep_state<'a>(&'a mut self, ty: std::any::TypeId, node: &'a dioxus_core::VNode<'a>, vdom: &'a dioxus_core::VirtualDom, children: &[&Self], ctx: &anymap::AnyMap) -> bool{
use dioxus_native_core::state::ChildDepState as _;
// println!("called update_child_dep_state with ty: {:?}", ty);
if false {
unreachable!()
}
#child_dep_state_fields
else{
panic!("{:?} not in {}", ty, #type_name_str)
}
}
fn child_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
let mut dep_types = Vec::new();
#(if #child_types::NODE_MASK.overlaps(mask) {
dep_types.push(std::any::TypeId::of::<#child_types>());
})*
dep_types
}
fn parent_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
let mut dep_types = Vec::new();
#(if #parent_types::NODE_MASK.overlaps(mask) {
dep_types.push(std::any::TypeId::of::<#parent_types>());
})*
dep_types
}
fn node_dep_types(&self, mask: &dioxus_native_core::state::NodeMask) -> Vec<std::any::TypeId>{
let mut dep_types = Vec::new();
#(if #node_types::NODE_MASK.overlaps(mask) {
dep_types.push(std::any::TypeId::of::<#node_types>());
})*
dep_types
}
};
gen.into()
}
};
gen.into()
Err(e) => e.into_compile_error().into(),
}
}
struct Struct {
name: Ident,
members: Vec<Member>,
}
impl Struct {
fn parse(fields: &[&Field]) -> Self {
fn new(name: Ident, fields: &[&Field]) -> Self {
let members = fields.iter().filter_map(|f| Member::parse(f)).collect();
Self { members }
Self { name, members }
}
}
@@ -215,57 +189,198 @@ struct StateStruct<'a> {
}
impl<'a> StateStruct<'a> {
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Self {
let state_members = strct
fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result<Self> {
let mut parse_err = Ok(());
let state_members: Vec<_> = strct
.members
.iter()
.zip(fields.iter())
.filter_map(|(m, f)| StateMember::parse(f, m, &strct))
.filter_map(|(m, f)| match StateMember::parse(f, m, &strct) {
Ok(m) => m,
Err(err) => {
parse_err = Err(err);
None
}
})
.collect();
parse_err?;
// todo: sort members
#[derive(Debug, Clone)]
struct DepNode<'a> {
state_mem: StateMember<'a>,
depandants: Vec<Box<DepNode<'a>>>,
}
impl<'a> DepNode<'a> {
fn new(state_mem: StateMember<'a>) -> Self {
Self {
state_mem,
depandants: Vec::new(),
}
}
Self { state_members }
}
}
/// flattens the node in pre order
fn flatten(self) -> Vec<StateMember<'a>> {
let DepNode {
state_mem,
depandants,
} = self;
let mut flat = vec![state_mem];
for d in depandants {
flat.append(&mut d.flatten());
}
flat
}
struct DepTypes {
ctx_ty: Option<Type>,
dep_ty: Option<Type>,
}
fn set_ids(&mut self, current_id: &mut usize) {
self.state_mem.member_id = dioxus_native_core::state::MemberId(*current_id);
// if the node depends on itself, we need to add the dependency seperately
if let Some(dep) = self.state_mem.dep_mem {
if dep == self.state_mem.mem {
self.state_mem
.dependants
.push((MemberId(*current_id), self.state_mem.dep_kind.clone()));
}
}
*current_id += 1;
for d in &mut self.depandants {
self.state_mem
.dependants
.push((MemberId(*current_id), d.state_mem.dep_kind.clone()));
d.set_ids(current_id);
}
}
impl Parse for DepTypes {
fn parse(input: ParseStream) -> Result<Self> {
let dep_ty = input.parse().ok();
let comma: Option<Token![,]> = input.parse().ok();
let ctx_ty = input.parse().ok();
Ok(Self {
ctx_ty: comma.and(ctx_ty),
dep_ty,
})
}
}
fn contains_member(&self, member: &Member) -> bool {
if self.state_mem.mem == member {
true
} else {
self.depandants.iter().any(|d| d.contains_member(member))
}
}
struct NodeDepTypes {
ctx_ty: Option<Type>,
}
// check if there are any mixed child/parent dependancies
fn check(&self) -> Option<Error> {
self.kind().err()
}
impl Parse for NodeDepTypes {
fn parse(input: ParseStream) -> Result<Self> {
let ctx_ty = input.parse().ok();
Ok(Self { ctx_ty })
}
}
fn kind(&self) -> Result<&DepKind> {
fn reduce_kind<'a>(dk1: &'a DepKind, dk2: &'a DepKind) -> Result<&'a DepKind> {
match (dk1, dk2) {
(DepKind::ChildDepState, DepKind::ParentDepState)
| (DepKind::ParentDepState, DepKind::ChildDepState) => Err(Error::new(
Span::call_site(),
"There is a ChildDepState that depends on a ParentDepState",
)),
// node dep state takes the lowest priority
(DepKind::NodeDepState, important) | (important, DepKind::NodeDepState) => {
Ok(important)
}
// they are the same
(fst, _) => Ok(fst),
}
}
reduce_kind(
self.depandants
.iter()
.try_fold(&DepKind::NodeDepState, |dk1, dk2| {
reduce_kind(dk1, dk2.kind()?)
})?,
&self.state_mem.dep_kind,
)
}
impl From<NodeDepTypes> for DepTypes {
fn from(node_dep_types: NodeDepTypes) -> Self {
Self {
ctx_ty: node_dep_types.ctx_ty,
dep_ty: None,
fn insert_dependant(&mut self, other: DepNode<'a>) -> bool {
let dep = other.state_mem.dep_mem.unwrap();
if self.contains_member(dep) {
if self.state_mem.mem == dep {
self.depandants.push(Box::new(other));
true
} else {
self.depandants
.iter_mut()
.find(|d| d.contains_member(dep))
.unwrap()
.insert_dependant(other)
}
} else {
false
}
}
}
// members need to be sorted so that members are updated after the members they depend on
let mut roots: Vec<DepNode> = vec![];
for m in state_members.into_iter() {
if let Some(dep) = m.dep_mem {
let root_depends_on = roots
.iter()
.filter_map(|m| m.state_mem.dep_mem)
.any(|d| m.mem == d);
if let Some(r) = roots.iter_mut().find(|r| r.contains_member(dep)) {
let new = DepNode::new(m);
if root_depends_on {
return Err(Error::new(
new.state_mem.mem.ident.span(),
format!("{} has a circular dependancy", new.state_mem.mem.ident),
));
}
// return Err(Error::new(new.state_mem.mem.ident.span(), "stuff"));
r.insert_dependant(new);
continue;
}
}
let mut new = DepNode::new(m);
let mut i = 0;
while i < roots.len() {
if roots[i].state_mem.dep_mem == Some(new.state_mem.mem) {
let child = roots.remove(i);
new.insert_dependant(child);
} else {
i += 1;
}
}
roots.push(new);
}
let mut current_id = 0;
for r in &mut roots {
r.set_ids(&mut current_id);
}
if let Some(err) = roots.iter().find_map(DepNode::check) {
Err(err)
} else {
let state_members: Vec<_> = roots
.into_iter()
.map(|r| r.flatten().into_iter())
.flatten()
.collect();
Ok(Self { state_members })
}
}
}
struct Dependancy {
ctx_ty: Option<Type>,
dep: Option<Ident>,
}
impl Parse for Dependancy {
fn parse(input: ParseStream) -> Result<Self> {
let dep = input
.parse()
.ok()
.filter(|i: &Ident| format!("{}", i) != "NONE");
let comma: Option<Token![,]> = input.parse().ok();
let ctx_ty = input.parse().ok();
Ok(Self {
ctx_ty: comma.and(ctx_ty),
dep,
})
}
}
#[derive(PartialEq, Debug)]
struct Member {
ty: Type,
ident: Ident,
@@ -280,16 +395,25 @@ impl Member {
}
}
#[derive(Debug, Clone)]
struct StateMember<'a> {
mem: &'a Member,
dep_kind: DepKind,
dep_mem: Option<&'a Member>,
ctx_ty: Option<Type>,
dependants: Vec<(dioxus_native_core::state::MemberId, DepKind)>,
// This is just the index of the final order of the struct it is used to communicate which parts need updated and what order to update them in.
member_id: dioxus_native_core::state::MemberId,
}
impl<'a> StateMember<'a> {
fn parse(field: &Field, mem: &'a Member, parent: &'a Struct) -> Option<StateMember<'a>> {
field.attrs.iter().find_map(|a| {
fn parse(
field: &Field,
mem: &'a Member,
parent: &'a Struct,
) -> Result<Option<StateMember<'a>>> {
let mut err = Ok(());
let member = field.attrs.iter().find_map(|a| {
let dep_kind = a
.path
.get_ident()
@@ -300,34 +424,44 @@ impl<'a> StateMember<'a> {
_ => None,
})
.flatten()?;
let deps: DepTypes = match dep_kind {
DepKind::NodeDepState => a.parse_args::<NodeDepTypes>().ok()?.into(),
_ => a.parse_args().ok()?,
};
Some(Self {
mem,
dep_kind,
dep_mem: deps
.dep_ty
.map(|ty| parent.members.iter().find(|m| m.ty == ty))
.flatten(),
ctx_ty: deps.ctx_ty,
})
})
match a.parse_args::<Dependancy>() {
Ok(dependancy) => {
let dep_mem = if let Some(name) = &dependancy.dep {
if let Some(found) = parent.members.iter().find(|m| &m.ident == name) {
Some(found)
} else {
err = Err(Error::new(
name.span(),
format!("{} not found in {}", name, parent.name),
));
None
}
} else {
None
};
Some(Self {
mem,
dep_kind,
dep_mem,
ctx_ty: dependancy.ctx_ty,
dependants: Vec::new(),
member_id: dioxus_native_core::state::MemberId(0),
})
}
Err(e) => {
err = Err(e);
None
}
}
});
err?;
Ok(member)
}
fn reduce_self(&self) -> quote::__private::TokenStream {
let ident = &self.mem.ident;
let get_ctx = if let Some(ctx_ty) = &self.ctx_ty {
if ctx_ty
== &Type::Tuple(TypeTuple {
paren_token: Paren {
span: quote::__private::Span::call_site(),
},
elems: Punctuated::new(),
})
{
if ctx_ty == &parse_quote!(()) {
quote! {&()}
} else {
let msg = ctx_ty.to_token_stream().to_string() + " not found in context";
@@ -336,40 +470,119 @@ impl<'a> StateMember<'a> {
} else {
quote! {&()}
};
let states_changed = {
let child_dep = self
.dependants
.iter()
.filter(|(_, kind)| kind == &DepKind::ChildDepState)
.map(|(id, _)| id.0);
let parent_dep = self
.dependants
.iter()
.filter(|(_, kind)| kind == &DepKind::ParentDepState)
.map(|(id, _)| id.0);
let node_dep = self
.dependants
.iter()
.filter(|(_, kind)| kind == &DepKind::NodeDepState)
.map(|(id, _)| id.0);
match self.dep_kind {
DepKind::NodeDepState => {
quote! {
dioxus_native_core::state::NodeStatesChanged{
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
}
}
}
DepKind::ChildDepState => {
quote! {
dioxus_native_core::state::ChildStatesChanged{
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
child_dep: &[#(dioxus_native_core::state::MemberId(#child_dep), )*],
}
}
}
DepKind::ParentDepState => {
quote! {
dioxus_native_core::state::ParentStatesChanged{
node_dep: &[#(dioxus_native_core::state::MemberId(#node_dep), )*],
parent_dep: &[#(dioxus_native_core::state::MemberId(#parent_dep), )*],
}
}
}
}
};
let ty = &self.mem.ty;
let node_view = quote!(NodeView::new(node, #ty::NODE_MASK, vdom));
let node_view =
quote!(dioxus_native_core::node_ref::NodeView::new(node, #ty::NODE_MASK, vdom));
let id = self.member_id.0;
if let Some(dep_ident) = &self.dep_mem.map(|m| &m.ident) {
match self.dep_kind {
DepKind::NodeDepState => {
quote!(self.#ident.reduce(#node_view, #get_ctx))
quote!({
// println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, &self.#dep_ident, #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
DepKind::ChildDepState => {
quote!(self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx))
quote!({
// println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, children.iter().map(|s| &s.#dep_ident), #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
DepKind::ParentDepState => {
quote!(self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx))
quote!({
// println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, parent.as_ref().map(|p| &p.#dep_ident), #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
}
} else {
match self.dep_kind {
DepKind::NodeDepState => {
quote!(self.#ident.reduce(#node_view, #get_ctx))
quote!({
// println!("node: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, &(), #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
DepKind::ChildDepState => {
quote!(self.#ident.reduce(#node_view, &(), #get_ctx))
quote!({
// println!("child: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, std::iter::empty(), #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
DepKind::ParentDepState => {
quote!(self.#ident.reduce(#node_view, Some(&()), #get_ctx))
quote!({
println!("parent: {:?} {:?} {:?}", self.#ident, #id, #node_view.id());
if self.#ident.reduce(#node_view, Some(&()), #get_ctx){
Some(#states_changed)
} else{
None
}
})
}
}
}
}
fn type_id(&self) -> quote::__private::TokenStream {
let ty = &self.mem.ty;
quote!({
let type_id = std::any::TypeId::of::<#ty>();
type_id
})
}
}
@@ -0,0 +1,28 @@
extern crate proc_macro;
use std::collections::BTreeMap;
use syn::{
self, bracketed,
parse::{Parse, ParseStream, Result},
LitStr, Token,
};
pub struct StrSlice {
pub map: BTreeMap<String, LitStr>,
}
impl Parse for StrSlice {
fn parse(input: ParseStream) -> Result<Self> {
let content;
bracketed!(content in input);
let mut map = BTreeMap::new();
while let Ok(s) = content.parse::<LitStr>() {
map.insert(s.value(), s);
#[allow(unused_must_use)]
{
content.parse::<Token![,]>();
}
}
Ok(StrSlice { map })
}
}