use super::control::Compatibility;
use heck::ToUpperCamelCase;
use strum::VariantArray as _;
macro_rules! def_enum {
(
$(#[$attr:meta])*
$vis:vis $name:ident => $ty:ty {
$( $(#[$v_attr:meta])* $variant:ident => $val:expr => $comment:literal),+
$(,)?
}
) => {
$(#[$attr])*
#[derive(PartialEq, Debug, Copy, Clone)]
#[non_exhaustive]
$vis struct $name($ty, &'static str, &'static str);
impl $name {
$(
$(#[$v_attr])*
#[doc = $comment]
$vis const $variant: Self = Self($val, stringify!($variant), $comment);
)+
}
impl strum::VariantArray for $name {
const VARIANTS: &'static [Self] = &[$(Self::$variant),+];
}
};
}
def_enum!(
pub Feature => Compatibility {
BASIC_PROTOCOL => Compatibility::Level(1) => "The original base protocol introduced in qcp v0.3.0",
NEW_RENO => Compatibility::Level(2) => "Support for the `NewReno` congestion control algorithm",
PRESERVE => Compatibility::Level(2) => "Support for preserving file metadata",
GET2_PUT2 => Compatibility::Level(2) => "Get2 and Put2 commands with extensible options.\n`FileHeaderV2` and `FileTrailerV2` structures with extensible metadata.",
CMSG_SMSG_2 => Compatibility::Level(3) => "Version 2 of `ClientMessage` and `ServerMessage` with extensible attributes.\n`CredentialsType` enum.",
MKDIR_SETMETA_LS => Compatibility::Level(4) => "CreateDirectory, SetMetadata, ListFiles commands",
}
);
impl Feature {
#[must_use]
pub const fn level(self) -> Compatibility {
self.0
}
#[must_use]
pub const fn name(&self) -> &'static str {
self.1
}
#[must_use]
pub const fn comment(&self) -> &'static str {
self.2
}
}
impl Compatibility {
#[must_use]
pub fn supports(self, feature: Feature) -> bool {
match self {
Compatibility::Unknown => false,
Compatibility::Newer => true,
Compatibility::Level(l) => l >= feature.level().into(),
}
}
}
#[derive(tabled::Tabled)]
struct TableRow {
#[tabled(rename = "Feature")]
name: String,
#[tabled(rename = "Level")]
compat: u16,
#[tabled(rename = "Notes")]
notes: String,
}
impl From<&Feature> for TableRow {
fn from(f: &Feature) -> Self {
Self {
name: f.name().to_upper_camel_case(),
compat: f.level().into(),
notes: f.comment().into(),
}
}
}
pub(crate) fn pretty_list() -> tabled::Table {
let data = Feature::VARIANTS.iter().map(TableRow::from);
tabled::Table::new(data)
}
#[cfg(test)]
mod test {
use crate::protocol::control::Compatibility;
use strum::VariantArray as _;
use super::Feature;
use heck::ToUpperCamelCase as _;
#[test]
fn list() {
for it in Feature::VARIANTS {
eprintln!(
"{} -> {} ({})",
it.name().to_upper_camel_case(),
it.level(),
u16::from(it.level())
);
}
}
#[test]
fn pretty() {
let tbl = super::pretty_list();
assert!(tbl.to_string().contains("BasicProtocol"));
}
#[test]
fn supports() {
assert!(Compatibility::Level(1).supports(Feature::BASIC_PROTOCOL));
assert!(Compatibility::Newer.supports(Feature::BASIC_PROTOCOL));
assert!(!Compatibility::Unknown.supports(Feature::BASIC_PROTOCOL));
assert!(!Compatibility::Level(1).supports(Feature::NEW_RENO));
assert!(Compatibility::Level(2).supports(Feature::NEW_RENO));
assert!(!Compatibility::Level(2).supports(Feature::CMSG_SMSG_2));
assert!(Compatibility::Level(3).supports(Feature::CMSG_SMSG_2));
}
}