diff options
| author | Andreu Botella | 2019-11-07 00:32:46 +0100 | 
|---|---|---|
| committer | Philipp A | 2019-11-07 09:27:38 +0100 | 
| commit | 5387291c1a2d4cfd0e5acdad26dcc7e33329d39a (patch) | |
| tree | 76df86d6b05af4d9dc2d7e036c1011b848f15f48 /src/parser/conversion/inline.rs | |
| parent | 10cc972f2e4b99e6d3082970ee6982bfee5211c0 (diff) | |
| download | rust-rst-5387291c1a2d4cfd0e5acdad26dcc7e33329d39a.tar.bz2 | |
Updating the parser to recognize most hyperlink references.
Diffstat (limited to 'src/parser/conversion/inline.rs')
| -rw-r--r-- | src/parser/conversion/inline.rs | 106 | 
1 files changed, 96 insertions, 10 deletions
| diff --git a/src/parser/conversion/inline.rs b/src/parser/conversion/inline.rs index 50a6258..c51b2d9 100644 --- a/src/parser/conversion/inline.rs +++ b/src/parser/conversion/inline.rs @@ -1,5 +1,6 @@  use failure::Error;  use pest::iterators::Pair; +use url::Url;  use crate::document_tree::{  	ExtraAttributes, @@ -20,31 +21,116 @@ use super::whitespace_normalize_name;  pub fn convert_inline(pair: Pair<Rule>) -> Result<c::TextOrInlineElement, Error> {  	Ok(match pair.as_rule() {  		Rule::str       		=> pair.as_str().into(), -		Rule::reference 		=> convert_reference(pair)?.into(), +		Rule::reference 		=> convert_reference(pair)?,  		Rule::substitution_ref 	=> convert_substitution(pair)?.into(),  		rule => unimplemented!("unknown rule {:?}", rule),  	})  } -fn convert_reference(pair: Pair<Rule>) -> Result<e::Reference, Error> { +fn convert_reference(pair: Pair<Rule>) -> Result<c::TextOrInlineElement, Error> {  	let name; -	let refuri = None; +	let refuri;  	let refid; -	let refname = vec![]; +	let mut refname = vec![]; +	let mut children: Vec<c::TextOrInlineElement> = vec![];  	let concrete = pair.into_inner().next().unwrap();  	match concrete.as_rule() {  		Rule::reference_target => {  			let rt_inner = concrete.into_inner().next().unwrap(); // reference_target_uq or target_name_qu -			refid = Some(rt_inner.as_str().into()); -			name  = Some(rt_inner.as_str().into()); +			match rt_inner.as_rule() { +				Rule::reference_target_uq => { +					refid  = None; +					name   = Some(rt_inner.as_str().into()); +					refuri = None; +					refname.push(rt_inner.as_str().into()); +					children.push(rt_inner.as_str().into()); +				}, +				Rule::reference_target_qu => { +					let (text, reference) = { +						let mut text = None; +						let mut reference = None; +						for inner in rt_inner.clone().into_inner() { +							match inner.as_rule() { +								Rule::reference_text => text = Some(inner), +								Rule::reference_bracketed => reference = Some(inner), +								_ => unreachable!() +							} +						} +						(text, reference) +					}; +					let trimmed_text = match (&text, &reference) { +						(Some(text), None) => text.as_str(), +						(_, Some(reference)) => { +							text +								.map(|text| text.as_str().trim_end_matches(|ch| " \n\r".contains(ch))) +								.filter(|text| !text.is_empty()) +								.unwrap_or_else(|| reference.clone().into_inner().next().unwrap().as_str()) +						} +						(None, None) => unreachable!() +					}; +					refid = None; +					name = Some(trimmed_text.into()); +					refuri = if let Some(reference) = reference { +						let inner = reference.into_inner().next().unwrap(); +						match inner.as_rule() { +							Rule::url => if let Ok(url) = Url::parse(inner.as_str()) { +								Some(url.into()) +							} else { +								unimplemented!("reference to a relative URL") +							}, +							Rule::target_name_qu => { +								refname.push(inner.as_str().into()); +								None +							}, +							Rule::relative_reference => unimplemented!("reference to a relative URL"), +							_ => unreachable!() +						} +					} else { +						refname.push(trimmed_text.into()); +						None +					}; +					children.push(trimmed_text.into()); +				}, +				_ => unreachable!() +			}  		},  		Rule::reference_explicit => unimplemented!("explicit reference"), -		Rule::reference_auto => unimplemented!("auto reference"), +		Rule::reference_auto => { +			let rt_inner = concrete.into_inner().next().unwrap(); +			match rt_inner.as_rule() { +				Rule::url_auto => match Url::parse(rt_inner.as_str()) { +					Ok(url) => { +						refuri = Some(url.into()); +						name   = None; +						refid  = None; +						children.push(rt_inner.as_str().into()); +					}, +					// if our parser got a URL wrong, return it as a string +					Err(_) => return Ok(rt_inner.as_str().into()) +				}, +				Rule::email => { +					let mailto_url = String::from("mailto:") + rt_inner.as_str(); +					match Url::parse(&mailto_url) { +						Ok(url) => { +							refuri = Some(url.into()); +							name   = None; +							refid  = None; +							children.push(rt_inner.as_str().into()); +						}, +						// if our parser got a URL wrong, return it as a string +						Err(_) => return Ok(rt_inner.as_str().into()) +					} +				}, +				_ => unreachable!() +			} +		},  		_ => unreachable!(),  	}; -	Ok(e::Reference::with_extra( -		a::Reference { name, refuri, refid, refname } -	)) +	Ok(e::Reference::new( +		Default::default(), +		a::Reference { name, refuri, refid, refname }, +		children +	).into())  }  fn convert_substitution(pair: Pair<Rule>) -> Result<e::SubstitutionReference, Error> { | 
