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
// SPEC.rs
// by Lut99
//
// Created:
// 28 Nov 2022, 15:56:23
// Last edited:
// 07 Nov 2023, 16:29:39
// Auto updated?
// Yes
//
// Description:
//! Defines (public) interfaces and structs in the `brane-cli` crate.
//
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter, Result as FResult};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use brane_exe::spec::CustomGlobalState;
use brane_tsk::docker::DockerOptions;
use parking_lot::Mutex;
use specifications::data::DataIndex;
use specifications::package::PackageIndex;
use specifications::version::Version;
use crate::errors::HostnameParseError;
/***** STATICS *****/
lazy_static::lazy_static! {
/// The default Docker API version that we're using.
pub static ref API_DEFAULT_VERSION: String = format!("{}", brane_tsk::docker::API_DEFAULT_VERSION);
}
/***** LIBRARY *****/
/// An auxillary struct that defines a hostname-only argument, optionally with some scheme.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Hostname {
/// The name of the host
pub hostname: String,
/// The scheme, if any.
pub scheme: Option<String>,
}
impl Hostname {
/// Constructor for the Hostname that creates it without any scheme.
///
/// # Arguments
/// - `hostname`: The hostname of the host to store in this struct.
///
/// # Returns
/// A new Hostname instance.
#[inline]
pub fn new(hostname: impl Into<String>) -> Self { Self { hostname: hostname.into(), scheme: None } }
/// Contsructor for the Hostname that creates it with the given hostname and scheme set.
///
/// # Arguments
/// - `hostname`: The hostname of the host to store in this struct.
/// - `scheme`: The scheme to connect to the host to.
///
/// # Returns
/// A new Hostname instance.
#[inline]
pub fn with_scheme(hostname: impl Into<String>, scheme: impl Into<String>) -> Self {
Self { hostname: hostname.into(), scheme: Some(scheme.into()) }
}
}
impl Display for Hostname {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
match &self.scheme {
Some(scheme) => write!(f, "{}://{}", scheme, self.hostname),
None => write!(f, "{}", self.hostname),
}
}
}
impl FromStr for Hostname {
type Err = HostnameParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// See if we can split the thing into a scheme and a non-scheme part
let scheme_sep_pos: Option<usize> = s.find("://");
let (scheme, hostname): (Option<String>, &str) = if let Some(scheme_sep_pos) = scheme_sep_pos {
// Split into the scheme and non-scheme
let scheme: &str = &s[..scheme_sep_pos];
let host: &str = &s[scheme_sep_pos + 3..];
// Assert the scheme only has alphanumeric characters
for c in scheme.chars() {
if !c.is_ascii_digit() && !c.is_ascii_lowercase() && !c.is_ascii_uppercase() {
return Err(HostnameParseError::IllegalSchemeChar { raw: scheme.into(), c });
}
}
// Return them
(Some(scheme.into()), host)
} else {
(None, s)
};
// Assert the host has no paths in it
if hostname.find('/').is_some() {
return Err(HostnameParseError::HostnameContainsPath { raw: hostname.into() });
}
// Alright good enough for now
Ok(Self { hostname: hostname.into(), scheme })
}
}
/// Parses a version number that scopes a particular operation down. In other words, can be a specific version number or `all`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VersionFix(pub Option<Version>);
impl Display for VersionFix {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult { write!(f, "{}", if let Some(version) = self.0 { version.to_string() } else { "all".into() }) }
}
impl FromStr for VersionFix {
type Err = specifications::version::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Parse the auto first
if s == "all" {
return Ok(Self(None));
}
// Otherwise, delegate to the version parser
Ok(Self(Some(Version::from_str(s)?)))
}
}
/// The global state for the OfflineVm.
#[derive(Clone, Debug)]
pub struct GlobalState {
/// The information we want to know for Docker
pub docker_opts: DockerOptions,
/// Whether to keep containers after execution or not
pub keep_containers: bool,
/// The path to the directory where packages (and thus container images) are stored for this session.
pub package_dir: PathBuf,
/// The path to the directory where datasets (where we wanna copy results) are stored for this session.
pub dataset_dir: PathBuf,
/// The path to the directory where intermediate results will be stored for this session.
pub results_dir: PathBuf,
/// The package index that contains info about each package.
pub pindex: Arc<PackageIndex>,
/// The data index that contains info about each package.
pub dindex: Arc<DataIndex>,
/// A list of results we planned in the previous timestep.
pub results: Arc<Mutex<HashMap<String, String>>>,
}
impl CustomGlobalState for GlobalState {}
/// The local state for the OfflineVm is unused.
pub type LocalState = ();