forked from lavina/lavina
				
			xmpp: rewrite bind request parser as a coroutine
This commit is contained in:
		
							parent
							
								
									87d73af811
								
							
						
					
					
						commit
						372efc3701
					
				|  | @ -78,89 +78,57 @@ impl Jid { | |||
| #[derive(PartialEq, Eq, Debug)] | ||||
| pub struct BindRequest(pub Resource); | ||||
| 
 | ||||
| pub struct BindRequestParser(BindRequestParserInner); | ||||
| 
 | ||||
| enum BindRequestParserInner { | ||||
|     Initial, | ||||
|     /// Consumed <bind> start and expects <resource>
 | ||||
|     InBind(Option<String>), | ||||
|     /// Consumed <resource> start
 | ||||
|     InBindResourceInitial, | ||||
|     /// Consumer <resource> start and inner text
 | ||||
|     InBindResourceEnd(String), | ||||
| } | ||||
| 
 | ||||
| impl FromXmlTag for BindRequest { | ||||
|     const NS: &'static str = XMLNS; | ||||
|     const NAME: &'static str = "bind"; | ||||
| } | ||||
| 
 | ||||
| impl FromXml for BindRequest { | ||||
|     type P = BindRequestParser; | ||||
|     type P = impl Parser<Output = Result<Self>>; | ||||
| 
 | ||||
|     fn parse() -> Self::P { | ||||
|         BindRequestParser(BindRequestParserInner::Initial) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO rewrite as a generator
 | ||||
| impl Parser for BindRequestParser { | ||||
|     type Output = Result<BindRequest>; | ||||
| 
 | ||||
|     fn consume<'a>( | ||||
|         self: Self, | ||||
|         namespace: ResolveResult, | ||||
|         event: &Event<'a>, | ||||
|     ) -> Continuation<Self, Self::Output> { | ||||
|         // TODO validate tag names and namespaces
 | ||||
|         use BindRequestParserInner::*; | ||||
|         match self.0 { | ||||
|             Initial => { | ||||
|                 let Event::Start(bytes) = event else { | ||||
|                     return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); | ||||
|                 }; | ||||
|                 if bytes.name().0 != BindRequest::NAME.as_bytes() { | ||||
|                     return Continuation::Final(Err(ffail!( | ||||
|                         "Unexpected XML tag: {:?}", | ||||
|                         bytes.name() | ||||
|                     ))); | ||||
|                 } | ||||
|                 let ResolveResult::Bound(Namespace(ns)) = namespace else { | ||||
|                     return Continuation::Final(Err(ffail!("No namespace provided"))); | ||||
|                 }; | ||||
|                 if ns != XMLNS.as_bytes() { | ||||
|                     return Continuation::Final(Err(ffail!("Incorrect namespace"))); | ||||
|                 } | ||||
|                 Continuation::Continue(BindRequestParser(InBind(None))) | ||||
|         |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { | ||||
|             let mut resource: Option<Box<[u8]>> = None; | ||||
|             let Event::Start(bytes) = event else { | ||||
|                 return Err(ffail!("Unexpected XML event: {event:?}")); | ||||
|             }; | ||||
|             if bytes.name().0 != BindRequest::NAME.as_bytes() { | ||||
|                 return Err(ffail!("Unexpected XML tag: {:?}", bytes.name())); | ||||
|             } | ||||
|             InBind(resource) => match event { | ||||
|                 Event::Start(bytes) => { | ||||
|                     Continuation::Continue(BindRequestParser(InBindResourceInitial)) | ||||
|                 } | ||||
|                 Event::End(bytes) => { | ||||
|                     let Some(resource) = resource else { | ||||
|                             return Continuation::Final(Err(ffail!("No resource was provided"))); | ||||
|                         }; | ||||
|                     Continuation::Final(Ok(BindRequest(Resource(resource.into())))) | ||||
|                 } | ||||
|                 _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), | ||||
|             }, | ||||
|             InBindResourceInitial => { | ||||
|                 let Event::Text(text) = event else { | ||||
|                     return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); | ||||
|                 }; | ||||
|                 let resource = match std::str::from_utf8(text.as_bytes()) { | ||||
|                     Ok(e) => e.to_string(), | ||||
|                     Err(err) => return Continuation::Final(Err(err.into())), | ||||
|                 }; | ||||
|                 Continuation::Continue(BindRequestParser(InBindResourceEnd(resource))) | ||||
|             let ResolveResult::Bound(Namespace(ns)) = namespace else { | ||||
|                 return Err(ffail!("No namespace provided")); | ||||
|             }; | ||||
|             if ns != XMLNS.as_bytes() { | ||||
|                 return Err(ffail!("Incorrect namespace")); | ||||
|             } | ||||
|             InBindResourceEnd(resource) => { | ||||
|                 let Event::End(bytes) = event else { | ||||
|                     return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); | ||||
|                 }; | ||||
|                 Continuation::Continue(BindRequestParser(InBind(Some(resource)))) | ||||
|             loop { | ||||
|                 let (namespace, event) = yield; | ||||
|                 match event { | ||||
|                     Event::Start(bytes) if bytes.name().0 == b"resource" => { | ||||
|                         let (namespace, event) = yield; | ||||
|                         if let Event::Text(text) = event { | ||||
|                             resource = Some(text.as_bytes().into()); | ||||
|                         } | ||||
|                         let (namespace, event) = yield; | ||||
|                         if let Event::End(_) = event { | ||||
|                         } else { | ||||
|                             return Err(ffail!("Unexpected XML event: {event:?}")); | ||||
|                         } | ||||
|                     } | ||||
|                     Event::End(bytes_) => { | ||||
|                         break; | ||||
|                     } | ||||
|                     _ => return Err(ffail!("Unexpected XML event: {event:?}")), | ||||
|                 } | ||||
|             } | ||||
|             let Some(resource) = resource else { | ||||
|                 return Err(ffail!("No resource was provided")); | ||||
|             }; | ||||
|             let resource = match std::str::from_utf8(resource.as_bytes()) { | ||||
|                 Ok(e) => e.to_string(), | ||||
|                 Err(err) => return Err(err.into()), | ||||
|             }; | ||||
|             Ok(BindRequest(Resource(resource.into()))) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue