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 212 213 214 215 216 217
/* VERSION.rs
* by Lut99
*
* Created:
* 08 May 2022, 13:31:16
* Last edited:
* 23 May 2022, 20:50:07
* Auto updated?
* Yes
*
* Description:
* Implements version queriers for the Brane framework.
**/
use std::str::FromStr;
use log::debug;
use reqwest::{Response, StatusCode};
use specifications::arch::Arch;
use specifications::version::Version;
use crate::errors::VersionError;
use crate::instance::InstanceInfo;
/***** HELPER STRUCTS *****/
/// Struct that is used in querying the local CLI.
#[derive(Debug)]
struct LocalVersion {
/// The architecture as reported by `uname -m`
arch: Arch,
/// The version as reported by the env
version: Version,
}
impl LocalVersion {
/// Constructor for the RemoteVersion.
///
/// Queries the CARGO_PKG_VERSION environment variable for the version.
///
/// # Returns
/// A new LocalVersion instance on success, or else a VersionError.
fn new() -> Result<Self, VersionError> {
// Parse the env
let version = match Version::from_str(env!("CARGO_PKG_VERSION")) {
Ok(version) => version,
Err(err) => {
return Err(VersionError::VersionParseError { raw: env!("CARGO_PKG_VERSION").to_string(), err });
},
};
// Done, return the struct
Ok(Self { arch: Arch::HOST, version })
}
}
/// Struct that is used in querying the remote CLI.
#[derive(Debug)]
struct RemoteVersion {
/// The architecture as reported by the remote
_arch: Arch,
/// The version as downloaded from the remote
version: Version,
}
impl RemoteVersion {
/// Constructor for the RemoteVersion.
///
/// Queries the remote host as stored in the Brane registry login file (get_config_dir()/registry.yml) for its version number.
///
/// # Returns
/// A new RemoteVersion instance on success, or else a VersionError.
async fn new() -> Result<Self, VersionError> {
debug!("Retrieving remote version number");
// Try to get the registry file path
debug!(" > Reading registy.yml...");
let config: InstanceInfo = match InstanceInfo::from_active_path() {
Ok(config) => config,
Err(err) => {
return Err(VersionError::InstanceInfoError { err });
},
};
// Pass to the other constructor
Self::from_instance_info(config).await
}
/// Constructor for the RemoteVersion, which creates it from a given IdentityFile.
///
/// # Arguments
/// - `info`: The InstanceInfo file to use to find the remote registry's properties.
///
/// # Returns
/// A new RemoteVersion instance on success, or else a VersionError.
async fn from_instance_info(info: InstanceInfo) -> Result<Self, VersionError> {
// Use reqwest for the API call
debug!(" > Querying...");
let mut url: String = info.api.to_string();
url.push_str("/version");
let response: Response = match reqwest::get(&url).await {
Ok(version) => version,
Err(err) => {
return Err(VersionError::RequestError { url, err });
},
};
if response.status() != StatusCode::OK {
return Err(VersionError::RequestFailure { url, status: response.status() });
}
let version_body: String = match response.text().await {
Ok(body) => body,
Err(err) => {
return Err(VersionError::RequestBodyError { url, err });
},
};
// Try to parse the version
debug!(" > Parsing remote version...");
let version = match Version::from_str(&version_body) {
Ok(version) => version,
Err(err) => {
return Err(VersionError::VersionParseError { raw: version_body, err });
},
};
// Done!
debug!("Remote version number: {}", &version);
Ok(Self { _arch: Arch::X86_64, version })
}
}
/***** HANDLERS *****/
/// Returns the local architecture (without any extra text).
pub fn handle_local_arch() -> Result<(), VersionError> {
// Get the local version and print it
println!("{}", LocalVersion::new()?.arch);
// Done
Ok(())
}
/// Returns the local version (without any extra text).
pub fn handle_local_version() -> Result<(), VersionError> {
// Get the local version and print it
println!("{}", LocalVersion::new()?.version);
// Done
Ok(())
}
/// Returns the local architecture (without any extra text).
pub async fn handle_remote_arch() -> Result<(), VersionError> {
// Get the remote version and print it
println!("<TBD>");
// Done
Ok(())
}
/// Returns the local version (without any extra text).
pub async fn handle_remote_version() -> Result<(), VersionError> {
// Get the remote version and print it
println!("{}", RemoteVersion::new().await?.version);
// Done
Ok(())
}
/// Returns both the local and possible remote version numbers with some pretty formatting.
pub async fn handle() -> Result<(), VersionError> {
// Get the local version first and immediately print
let local = LocalVersion::new()?;
println!();
println!("Brane CLI client");
println!(" - Version : v{}", local.version);
println!(" - Architecture : {}", local.arch);
println!();
// If the registry file exists, then also do the remote
let active_instance_exists: bool = match InstanceInfo::active_instance_exists() {
Ok(exists) => exists,
Err(err) => {
return Err(VersionError::InstanceInfoExistsError { err });
},
};
if active_instance_exists {
// Get the registry file from it
let config = match InstanceInfo::from_active_path() {
Ok(config) => config,
Err(err) => {
return Err(VersionError::InstanceInfoError { err });
},
};
// Print the URL
println!("Remote Brane instance at '{}'", &config.api);
// Get the version
let remote = RemoteVersion::from_instance_info(config).await?;
println!(" - Version : v{}", remote.version);
println!(" - Architecture : <TBD>");
println!();
}
// Done
Ok(())
}