diff options
| author | Philipp A | 2019-12-26 23:01:00 +0100 | 
|---|---|---|
| committer | Philipp A | 2019-12-26 23:36:48 +0100 | 
| commit | a0e3c53758d526bb418c068bce1c99fa5a597ed3 (patch) | |
| tree | e640238b011a9ea7806ccccaf1a435e4b371a376 /document_tree/src/url.rs | |
| parent | 7018f5d3c42f18b6c83f398db9f1915361a7c679 (diff) | |
| download | rust-rst-a0e3c53758d526bb418c068bce1c99fa5a597ed3.tar.bz2 | |
Split into smaller crates
Diffstat (limited to 'document_tree/src/url.rs')
| -rw-r--r-- | document_tree/src/url.rs | 78 | 
1 files changed, 78 insertions, 0 deletions
| diff --git a/document_tree/src/url.rs b/document_tree/src/url.rs new file mode 100644 index 0000000..31a0536 --- /dev/null +++ b/document_tree/src/url.rs @@ -0,0 +1,78 @@ +use std::fmt; +use std::str::FromStr; + +use url::{self,ParseError}; +use serde_derive::Serialize; + + +fn starts_with_scheme(input: &str) -> bool { +	let scheme = input.split(':').next().unwrap(); +	if scheme == input || scheme.is_empty() { +		return false; +	} +	let mut chars = input.chars(); +	// First character. +	if !chars.next().unwrap().is_ascii_alphabetic() { +		return false; +	} +	for ch in chars { +		if !ch.is_ascii_alphanumeric() && ch != '+' && ch != '-' && ch != '.' { +			return false; +		} +	} +	true +} + +/// The string representation of a URL, either absolute or relative, that has +/// been verified as a valid URL on construction. +#[derive(Debug,PartialEq,Serialize,Clone)] +#[serde(transparent)] +pub struct Url(String); + +impl Url { +	pub fn parse_absolute(input: &str) -> Result<Self, ParseError> { +		Ok(url::Url::parse(input)?.into()) +	} +	pub fn parse_relative(input: &str) -> Result<Self, ParseError> { +		// We're assuming that any scheme through which RsT documents are being +		// accessed is a hierarchical scheme, and so we can parse relative to a +		// random hierarchical URL. +		if input.starts_with('/') || !starts_with_scheme(input) { +			// Continue only if the parse succeeded, disregarding its result. +			let random_base_url = url::Url::parse("https://a/b").unwrap(); +			url::Url::options() +				.base_url(Some(&random_base_url)) +				.parse(input)?; +			Ok(Url(input.into())) +		} else { +			// If this is a URL at all, it's an absolute one. +			// There's no appropriate variant of url::ParseError really. +			Err(ParseError::SetHostOnCannotBeABaseUrl) +		} +	} +	pub fn as_str(&self) -> &str { +		self.0.as_str() +	} +} + +impl From<url::Url> for Url { +	fn from(url: url::Url) -> Self { +		Url(url.into_string()) +	} +} + + +impl fmt::Display for Url { +	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +		write!(f, "{}", self.as_str()) +	} +} + + +impl FromStr for Url { +	type Err = ParseError; +	fn from_str(input: &str) -> Result<Self, Self::Err> { +		Url::parse_absolute(input) +			.or_else(|_| Url::parse_relative(input)) +	} +} | 
