legacympt-rs/addonscript/src/manifest/link.rs
2021-08-29 15:14:10 +02:00

82 lines
2.1 KiB
Rust

use serde::{de::Visitor, Deserialize, Serialize};
use std::str::FromStr;
use thiserror::Error;
use url::Url;
use std::path::PathBuf;
#[derive(Debug)]
pub enum Link {
File(PathBuf),
Http(Url),
}
#[derive(Debug, Error)]
pub enum LinkParseError {
#[error("Unknown protocol")]
UnkownProtocol,
#[error("Error parsing url: {0}")]
UrlParseError(url::ParseError),
}
impl FromStr for Link {
type Err = LinkParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with("file://") {
Ok(Link::File(PathBuf::from(s.trim_start_matches("file://"))))
} else if s.starts_with("http://") || s.starts_with("https://") {
s.parse::<Url>()
.map(Link::Http)
.map_err(LinkParseError::UrlParseError)
} else {
Err(LinkParseError::UnkownProtocol)
}
}
}
impl ToString for Link {
fn to_string(&self) -> String {
match self {
Link::File(path) => format!("file://{}", path.to_string_lossy()),
Link::Http(url) => url.to_string(),
}
}
}
impl Serialize for Link {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Link {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Vis;
impl<'de> Visitor<'de> for Vis {
type Value = Link;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(
"either a (somewhat invalid) relative file:// url or a http/https url",
)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
v.parse::<Link>()
.map_err(|e| E::custom(format!("invalid link: {}", e)))
}
}
deserializer.deserialize_str(Vis)
}
}