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() }
}