forked from lavina/lavina
				
			xmpp: rewrite bind request parser as a coroutine (#12)
Reviewed-on: lavina/lavina#12
This commit is contained in:
		
							parent
							
								
									444b608e96
								
							
						
					
					
						commit
						1b3551f108
					
				|  | @ -78,90 +78,57 @@ impl Jid { | ||||||
| #[derive(PartialEq, Eq, Debug)] | #[derive(PartialEq, Eq, Debug)] | ||||||
| pub struct BindRequest(pub Resource); | 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 { | impl FromXmlTag for BindRequest { | ||||||
|     const NS: &'static str = XMLNS; |     const NS: &'static str = XMLNS; | ||||||
|     const NAME: &'static str = "bind"; |     const NAME: &'static str = "bind"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FromXml for BindRequest { | impl FromXml for BindRequest { | ||||||
|     type P = BindRequestParser; |     type P = impl Parser<Output = Result<Self>>; | ||||||
| 
 | 
 | ||||||
|     fn parse() -> Self::P { |     fn parse() -> Self::P { | ||||||
|         BindRequestParser(BindRequestParserInner::Initial) |         |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { | ||||||
|     } |             let mut resource: Option<Str> = None; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 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 { |             let Event::Start(bytes) = event else { | ||||||
|                     return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); |                 return Err(anyhow!("Unexpected XML event: {event:?}")); | ||||||
|             }; |             }; | ||||||
|             if bytes.name().0 != BindRequest::NAME.as_bytes() { |             if bytes.name().0 != BindRequest::NAME.as_bytes() { | ||||||
|                     return Continuation::Final(Err(anyhow!( |                 return Err(anyhow!("Unexpected XML tag: {:?}", bytes.name())); | ||||||
|                         "Unexpected XML tag: {:?}", |  | ||||||
|                         bytes.name() |  | ||||||
|                     ))); |  | ||||||
|             } |             } | ||||||
|             let ResolveResult::Bound(Namespace(ns)) = namespace else { |             let ResolveResult::Bound(Namespace(ns)) = namespace else { | ||||||
|                     return Continuation::Final(Err(anyhow!("No namespace provided"))); |                 return Err(anyhow!("No namespace provided")); | ||||||
|             }; |             }; | ||||||
|             if ns != XMLNS.as_bytes() { |             if ns != XMLNS.as_bytes() { | ||||||
|                     return Continuation::Final(Err(anyhow!("Incorrect namespace"))); |                 return Err(anyhow!("Incorrect namespace")); | ||||||
|             } |             } | ||||||
|                 Continuation::Continue(BindRequestParser(InBind(None))) |             loop { | ||||||
|             } |                 let (namespace, event) = yield; | ||||||
|             InBind(resource) => match event { |                 match event { | ||||||
|                 Event::Start(bytes) => { |                     Event::Start(bytes) if bytes.name().0 == b"resource" => { | ||||||
|                     Continuation::Continue(BindRequestParser(InBindResourceInitial)) |                         let (namespace, event) = yield; | ||||||
|                 } |  | ||||||
|                 Event::End(bytes) => { |  | ||||||
|                     let Some(resource) = resource else { |  | ||||||
|                             return Continuation::Final(Err(anyhow!("No resource was provided"))); |  | ||||||
|                         }; |  | ||||||
|                     Continuation::Final(Ok(BindRequest(Resource(resource.into())))) |  | ||||||
|                 } |  | ||||||
|                 _ => Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))), |  | ||||||
|             }, |  | ||||||
|             InBindResourceInitial => { |  | ||||||
|                         let Event::Text(text) = event else { |                         let Event::Text(text) = event else { | ||||||
|                     return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); |                             return Err(anyhow!("Unexpected XML event: {event:?}")); | ||||||
|                         }; |                         }; | ||||||
|                 let resource = match std::str::from_utf8(&*text) { |                         resource = Some(std::str::from_utf8(&*text)?.into()); | ||||||
|                     Ok(e) => e.to_string(), |                         let (namespace, event) = yield; | ||||||
|                     Err(err) => return Continuation::Final(Err(err.into())), |  | ||||||
|                 }; |  | ||||||
|                 Continuation::Continue(BindRequestParser(InBindResourceEnd(resource))) |  | ||||||
|             } |  | ||||||
|             InBindResourceEnd(resource) => { |  | ||||||
|                         let Event::End(bytes) = event else { |                         let Event::End(bytes) = event else { | ||||||
|                     return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); |                             return Err(anyhow!("Unexpected XML event: {event:?}")); | ||||||
|                         }; |                         }; | ||||||
|                 Continuation::Continue(BindRequestParser(InBind(Some(resource)))) |                         if bytes.name().0 != b"resource" { | ||||||
|  |                             return Err(anyhow!("Unexpected XML tag: {:?}", bytes.name())); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |                     Event::End(bytes) if bytes.name().0 == BindRequest::NAME.as_bytes() => { | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                     _ => return Err(anyhow!("Unexpected XML event: {event:?}")), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             let Some(resource) = resource else { | ||||||
|  |                 return Err(anyhow!("No resource was provided")); | ||||||
|  |             }; | ||||||
|  |             Ok(BindRequest(Resource(resource))) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue