brane_cc/spec.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
// SPEC.rs
// by Lut99
//
// Created:
// 18 Nov 2022, 15:03:19
// Last edited:
// 12 Dec 2022, 17:02:27
// Auto updated?
// Yes
//
// Description:
//! Defines (public) interfaces and structs for the `brane-cc` crate.
//
use std::fmt::{Display, Formatter, Result as FResult};
use std::path::PathBuf;
use std::str::FromStr;
use enum_debug::EnumDebug;
use url::Url;
use crate::errors::IndexLocationParseError;
/***** CONSTANTS *****/
/// The prefix for a local IndexLocation.
pub const LOCAL_PREFIX: &str = "Local<";
/// The postfix for a local IndexLocation.
pub const LOCAL_POSTFIX: &str = ">";
/// The prefix for a remote IndexLocation.
pub const REMOTE_PREFIX: &str = "Remote<";
/// The postfix for a remote IndexLocation.
pub const REMOTE_POSTFIX: &str = ">";
/***** LIBRARY *****/
/// Defins a formatter for the IndexLocation that writes it in a `IndexLocation::FromStr`-compatible way.
#[derive(Debug)]
pub struct IndexLocationSerializer<'a> {
/// The index location to serialize.
loc: &'a IndexLocation,
}
impl<'a> Display for IndexLocationSerializer<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use IndexLocation::*;
match self.loc {
Local(path) => write!(f, "{}{}{}", LOCAL_PREFIX, path.display(), LOCAL_POSTFIX),
Remote(addr) => write!(f, "{REMOTE_PREFIX}{addr}{REMOTE_POSTFIX}"),
}
}
}
/// Defines an enum that either defines a local path to fetch packages / datasets from, or a remote location to fetch packages / datasets from.
#[derive(Clone, Debug, EnumDebug, Eq, Hash, PartialEq)]
pub enum IndexLocation {
/// It's a local location
Local(PathBuf),
/// It's a remote address
Remote(String),
}
impl IndexLocation {
/// Returns a formatter for the IndexLocation that writes it in an unambigious, serialized way.
#[inline]
pub fn serialize(&self) -> IndexLocationSerializer { IndexLocationSerializer { loc: self } }
/// Returns whether this index location is a local path.
#[inline]
pub fn is_local(&self) -> bool { matches!(self, Self::Local(_)) }
/// Returns the path in this IndexLocation as if it is a Local location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Local`.
#[inline]
pub fn local(&self) -> &PathBuf {
if let Self::Local(path) = self {
path
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Local", self.variant());
}
}
/// Returns a mutable path in this IndexLocation as if it is a Local location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Local`.
#[inline]
pub fn local_mut(&mut self) -> &mut PathBuf {
if let Self::Local(path) = self {
path
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Local", self.variant());
}
}
/// Consumes this IndexLocation into a local path as if it is a Local location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Local`.
#[inline]
pub fn into_local(self) -> PathBuf {
if let Self::Local(path) = self {
path
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Local", self.variant());
}
}
/// Returns whether this index location is a remote address.
#[inline]
pub fn is_remote(&self) -> bool { matches!(self, Self::Remote(_)) }
/// Returns the address in this IndexLocation as if it is a Remote location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Remote`.
#[inline]
pub fn remote(&self) -> &String {
if let Self::Remote(addr) = self {
addr
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Remote", self.variant());
}
}
/// Returns a mutable address in this IndexLocation as if it is a Remote location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Remote`.
#[inline]
pub fn remote_mut(&mut self) -> &mut String {
if let Self::Remote(addr) = self {
addr
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Remote", self.variant());
}
}
/// Consumes this IndexLocation into a remote address as if it is a Remote location.
///
/// # Panics
/// This function will panic if `self` is not a `Self::Remote`.
#[inline]
pub fn into_remote(self) -> String {
if let Self::Remote(addr) = self {
addr
} else {
panic!("Cannot unwrap {:?} as an IndexLocation::Remote", self.variant());
}
}
}
impl Display for IndexLocation {
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use IndexLocation::*;
match self {
Local(path) => write!(f, "{}", path.display()),
Remote(addr) => write!(f, "{addr}"),
}
}
}
impl AsRef<IndexLocation> for IndexLocation {
#[inline]
fn as_ref(&self) -> &Self { self }
}
impl From<&IndexLocation> for IndexLocation {
#[inline]
fn from(value: &IndexLocation) -> Self { value.clone() }
}
impl From<&mut IndexLocation> for IndexLocation {
#[inline]
fn from(value: &mut IndexLocation) -> Self { value.clone() }
}
impl FromStr for IndexLocation {
type Err = IndexLocationParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// First, attempt to find the "unambigious patterns"
if s.len() > LOCAL_PREFIX.len()
&& &s[..LOCAL_PREFIX.len()] == LOCAL_PREFIX
&& s.len() > LOCAL_POSTFIX.len()
&& &s[s.len() - LOCAL_POSTFIX.len()..] == LOCAL_POSTFIX
{
// The bit in between is the local path
return Ok(Self::Local(PathBuf::from(&s[LOCAL_PREFIX.len()..s.len() - LOCAL_POSTFIX.len()])));
}
if s.len() > REMOTE_PREFIX.len()
&& &s[..REMOTE_PREFIX.len()] == REMOTE_PREFIX
&& s.len() > REMOTE_POSTFIX.len()
&& &s[s.len() - REMOTE_POSTFIX.len()..] == REMOTE_POSTFIX
{
// The bit in between is the remote address
return Ok(Self::Remote(s[REMOTE_PREFIX.len()..s.len() - REMOTE_POSTFIX.len()].into()));
}
// Next, if we can parse it as an address, use remote
if Url::parse(s).is_ok() { Ok(Self::Remote(s.into())) } else { Ok(Self::Local(s.into())) }
}
}
impl<T: AsRef<str>> From<T> for IndexLocation {
fn from(value: T) -> Self { Self::from_str(value.as_ref()).unwrap() }
}