This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-xrpc / src / body.rs
3.9 kB 132 lines
1use serde::Deserialize; 2use serde::de::{self, Deserializer}; 3 4use knot_types::{AtUri, RefName, RepoName}; 5use url::Url; 6 7pub(crate) struct RepoAtUri(AtUri<String>); 8 9impl RepoAtUri { 10 pub(crate) fn at_uri(&self) -> &AtUri<String> { 11 &self.0 12 } 13} 14 15impl<'de> Deserialize<'de> for RepoAtUri { 16 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 17 let raw = String::deserialize(deserializer)?; 18 AtUri::new_owned(raw) 19 .map(RepoAtUri) 20 .map_err(|_| de::Error::custom("repo must be an at-uri")) 21 } 22} 23 24pub(crate) struct RepoNameArg(RepoName); 25 26impl RepoNameArg { 27 pub(crate) fn as_str(&self) -> &str { 28 self.0.as_str() 29 } 30} 31 32impl<'de> Deserialize<'de> for RepoNameArg { 33 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 34 let raw = String::deserialize(deserializer)?; 35 RepoName::new(raw) 36 .map(RepoNameArg) 37 .map_err(de::Error::custom) 38 } 39} 40 41#[derive(Clone)] 42pub(crate) struct SourceUrl(Url); 43 44impl SourceUrl { 45 pub(crate) fn parse(raw: &str) -> Result<Self, &'static str> { 46 parse_source_url(raw) 47 } 48 49 pub(crate) fn as_str(&self) -> &str { 50 self.0.as_str() 51 } 52 53 pub(crate) fn as_url(&self) -> &Url { 54 &self.0 55 } 56} 57 58impl<'de> Deserialize<'de> for SourceUrl { 59 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 60 let raw = String::deserialize(deserializer)?; 61 parse_source_url(&raw).map_err(de::Error::custom) 62 } 63} 64 65fn parse_source_url(raw: &str) -> Result<SourceUrl, &'static str> { 66 let url = Url::parse(raw).map_err(|_| "source must be a valid url")?; 67 match matches!(url.scheme(), "http" | "https") && url.has_host() { 68 true => Ok(SourceUrl(url)), 69 false => Err("source must be an http or https url"), 70 } 71} 72 73// A sourceless repo is a plain repo not a bad request necessarily, 74// so empty string / missing field both have to make `None`. 75pub(crate) fn optional_source_url<'de, D: Deserializer<'de>>( 76 deserializer: D, 77) -> Result<Option<SourceUrl>, D::Error> { 78 match Option::<String>::deserialize(deserializer)?.as_deref() { 79 None | Some("") => Ok(None), 80 Some(raw) => parse_source_url(raw).map(Some).map_err(de::Error::custom), 81 } 82} 83 84knot_types::text_newtype! { 85 pub(crate) struct Patch(String) => verbatim; 86 pub(crate) struct CommitMessage(String) => verbatim; 87 pub(crate) struct CommitBody(String) => verbatim; 88} 89 90pub(crate) use crate::query::Revspec; 91 92pub(crate) struct ForkRef(RefName); 93 94impl ForkRef { 95 pub(crate) fn hidden_ref(&self, remote: &RemoteRef) -> Option<RefName> { 96 RefName::new(format!("{}/{}", self.0.as_str(), remote.as_str())).ok() 97 } 98} 99 100// Comes as a bare name, 101// and `refs/hidden/` is the only place a fork staging ref is allowed to exist, 102// so `Deserialize` prepends the prefix & `RefName::new` validates the whole string, 103// therefore there's nothing to append after check. 104impl<'de> Deserialize<'de> for ForkRef { 105 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 106 let raw = String::deserialize(deserializer)?; 107 RefName::new(format!("refs/hidden/{raw}")) 108 .map(ForkRef) 109 .map_err(|_| de::Error::custom("invalid fork ref")) 110 } 111} 112 113pub(crate) struct RemoteRef(knot_types::BranchName); 114 115impl RemoteRef { 116 pub(crate) fn as_str(&self) -> &str { 117 self.0.as_str() 118 } 119 120 pub(crate) fn head_ref(&self) -> RefName { 121 self.0.head_ref() 122 } 123} 124 125impl<'de> Deserialize<'de> for RemoteRef { 126 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 127 let raw = String::deserialize(deserializer)?; 128 knot_types::BranchName::new(raw) 129 .map(RemoteRef) 130 .map_err(|_| de::Error::custom("invalid remote ref")) 131 } 132}