Add AlkTypeKind::to_bast_str/from_bast_str for BAST kind strings
Step 1 of the BAST pivot. Adds the lowercase-string mapping
("uint32" <-> AlkTypeKind::Uint32) that the BAST parser and
validator dispatch on (D-BAST-002).
- to_bast_str(self) -> &'static str: returns the lowercase BAST
string for all 19 variants (14 primitives + struct/union/array/
record/enum). Boolean -> "bool", distinct from the PascalCase
variant name.
- from_bast_str(s) -> Result<AlkTypeKind, AlkTypeError>: inverse of
to_bast_str; returns AlkTypeError::Schema for unknown strings.
These are additive inherent methods on the already-re-exported enum.
The existing FromStr impl (parsing the v0.1.0 "AlkType:Uint32"
keyword form) is unchanged and removed in step 8. The two surfaces
are deliberately distinct: from_bast_str rejects "AlkType:Uint32"
and FromStr rejects "uint32".
Verification:
- cargo test --release: 402 tests pass (316 lib + 86 integration);
6 new unit tests cover both directions, round-trip, and the
from_bast_str/from_str distinctness.
- cargo clippy --all-targets -- -D warnings: clean.
This commit is contained in:
1 parent
f5f52c61e8
commit
66ab9d7d93
1 file changed
+163
+163
@@ -173,6 +173,69 @@ impl AlkTypeKind {
|
||||
| AlkTypeKind::Record
|
||||
)
|
||||
}
|
||||
|
||||
/// The lowercase BAST kind string, e.g. `"uint32"` (D-BAST-002).
|
||||
///
|
||||
/// This is the string used in BAST documents (`{ "kind": "uint32" }`)
|
||||
/// and the value the parser and validator dispatch on. Distinct from
|
||||
/// [`as_str`](Self::as_str), which returns the v0.1.0 `AlkType:Uint32`
|
||||
/// keyword form.
|
||||
pub fn to_bast_str(self) -> &'static str {
|
||||
match self {
|
||||
AlkTypeKind::Int8 => "int8",
|
||||
AlkTypeKind::Int16 => "int16",
|
||||
AlkTypeKind::Int32 => "int32",
|
||||
AlkTypeKind::Int64 => "int64",
|
||||
AlkTypeKind::Uint8 => "uint8",
|
||||
AlkTypeKind::Uint16 => "uint16",
|
||||
AlkTypeKind::Uint32 => "uint32",
|
||||
AlkTypeKind::Uint64 => "uint64",
|
||||
AlkTypeKind::Float32 => "float32",
|
||||
AlkTypeKind::Float64 => "float64",
|
||||
AlkTypeKind::Boolean => "bool",
|
||||
AlkTypeKind::String => "string",
|
||||
AlkTypeKind::Bytes => "bytes",
|
||||
AlkTypeKind::Timestamp => "timestamp",
|
||||
AlkTypeKind::Enum => "enum",
|
||||
AlkTypeKind::Struct => "struct",
|
||||
AlkTypeKind::Union => "union",
|
||||
AlkTypeKind::Array => "array",
|
||||
AlkTypeKind::Record => "record",
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a lowercase BAST kind string into an `AlkTypeKind`
|
||||
/// (D-BAST-002). Returns `AlkTypeError::Schema` for unknown strings.
|
||||
///
|
||||
/// This is the inverse of [`to_bast_str`](Self::to_bast_str) and is
|
||||
/// distinct from the v0.1.0 `FromStr` impl, which parses the
|
||||
/// `AlkType:Uint32` keyword form.
|
||||
pub fn from_bast_str(s: &str) -> Result<AlkTypeKind, AlkTypeError> {
|
||||
match s {
|
||||
"int8" => Ok(AlkTypeKind::Int8),
|
||||
"int16" => Ok(AlkTypeKind::Int16),
|
||||
"int32" => Ok(AlkTypeKind::Int32),
|
||||
"int64" => Ok(AlkTypeKind::Int64),
|
||||
"uint8" => Ok(AlkTypeKind::Uint8),
|
||||
"uint16" => Ok(AlkTypeKind::Uint16),
|
||||
"uint32" => Ok(AlkTypeKind::Uint32),
|
||||
"uint64" => Ok(AlkTypeKind::Uint64),
|
||||
"float32" => Ok(AlkTypeKind::Float32),
|
||||
"float64" => Ok(AlkTypeKind::Float64),
|
||||
"bool" => Ok(AlkTypeKind::Boolean),
|
||||
"string" => Ok(AlkTypeKind::String),
|
||||
"bytes" => Ok(AlkTypeKind::Bytes),
|
||||
"timestamp" => Ok(AlkTypeKind::Timestamp),
|
||||
"enum" => Ok(AlkTypeKind::Enum),
|
||||
"struct" => Ok(AlkTypeKind::Struct),
|
||||
"union" => Ok(AlkTypeKind::Union),
|
||||
"array" => Ok(AlkTypeKind::Array),
|
||||
"record" => Ok(AlkTypeKind::Record),
|
||||
other => Err(AlkTypeError::Schema(format!(
|
||||
"unknown BAST kind: {other}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AlkTypeKind {
|
||||
@@ -1084,4 +1147,104 @@ mod tests {
|
||||
let schema = json!({"AlkType:Uint32": true});
|
||||
assert_eq!(get_alktype_kind_enum(&schema), Some(AlkTypeKind::Uint32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_bast_str_covers_all_19_variants() {
|
||||
let cases: &[(AlkTypeKind, &str)] = &[
|
||||
(AlkTypeKind::Int8, "int8"),
|
||||
(AlkTypeKind::Int16, "int16"),
|
||||
(AlkTypeKind::Int32, "int32"),
|
||||
(AlkTypeKind::Int64, "int64"),
|
||||
(AlkTypeKind::Uint8, "uint8"),
|
||||
(AlkTypeKind::Uint16, "uint16"),
|
||||
(AlkTypeKind::Uint32, "uint32"),
|
||||
(AlkTypeKind::Uint64, "uint64"),
|
||||
(AlkTypeKind::Float32, "float32"),
|
||||
(AlkTypeKind::Float64, "float64"),
|
||||
(AlkTypeKind::Boolean, "bool"),
|
||||
(AlkTypeKind::String, "string"),
|
||||
(AlkTypeKind::Bytes, "bytes"),
|
||||
(AlkTypeKind::Timestamp, "timestamp"),
|
||||
(AlkTypeKind::Enum, "enum"),
|
||||
(AlkTypeKind::Struct, "struct"),
|
||||
(AlkTypeKind::Union, "union"),
|
||||
(AlkTypeKind::Array, "array"),
|
||||
(AlkTypeKind::Record, "record"),
|
||||
];
|
||||
assert_eq!(cases.len(), 19);
|
||||
for (kind, expected) in cases {
|
||||
assert_eq!(kind.to_bast_str(), *expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_bast_str_round_trips_all_variants() {
|
||||
for kind in [
|
||||
AlkTypeKind::Int8,
|
||||
AlkTypeKind::Int16,
|
||||
AlkTypeKind::Int32,
|
||||
AlkTypeKind::Int64,
|
||||
AlkTypeKind::Uint8,
|
||||
AlkTypeKind::Uint16,
|
||||
AlkTypeKind::Uint32,
|
||||
AlkTypeKind::Uint64,
|
||||
AlkTypeKind::Float32,
|
||||
AlkTypeKind::Float64,
|
||||
AlkTypeKind::Boolean,
|
||||
AlkTypeKind::String,
|
||||
AlkTypeKind::Bytes,
|
||||
AlkTypeKind::Timestamp,
|
||||
AlkTypeKind::Enum,
|
||||
AlkTypeKind::Struct,
|
||||
AlkTypeKind::Union,
|
||||
AlkTypeKind::Array,
|
||||
AlkTypeKind::Record,
|
||||
] {
|
||||
let s = kind.to_bast_str();
|
||||
let parsed = AlkTypeKind::from_bast_str(s).expect("round-trip {s}");
|
||||
assert_eq!(parsed, kind, "round-trip {s}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_bast_str_parses_lowercase_strings() {
|
||||
assert!(matches!(
|
||||
AlkTypeKind::from_bast_str("uint32"),
|
||||
Ok(AlkTypeKind::Uint32)
|
||||
));
|
||||
assert!(matches!(
|
||||
AlkTypeKind::from_bast_str("bool"),
|
||||
Ok(AlkTypeKind::Boolean)
|
||||
));
|
||||
assert!(matches!(
|
||||
AlkTypeKind::from_bast_str("struct"),
|
||||
Ok(AlkTypeKind::Struct)
|
||||
));
|
||||
assert!(matches!(
|
||||
AlkTypeKind::from_bast_str("timestamp"),
|
||||
Ok(AlkTypeKind::Timestamp)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_bast_str_rejects_unknown_strings() {
|
||||
assert!(AlkTypeKind::from_bast_str("uint128").is_err());
|
||||
assert!(AlkTypeKind::from_bast_str("Uint32").is_err());
|
||||
assert!(AlkTypeKind::from_bast_str("AlkType:Uint32").is_err());
|
||||
assert!(AlkTypeKind::from_bast_str("").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_bast_str_distinct_from_fromstr_keyword_form() {
|
||||
assert!("AlkType:Uint32".parse::<AlkTypeKind>().is_ok());
|
||||
assert!(AlkTypeKind::from_bast_str("AlkType:Uint32").is_err());
|
||||
assert!(AlkTypeKind::from_bast_str("uint32").is_ok());
|
||||
assert!("uint32".parse::<AlkTypeKind>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_bast_str_distinct_from_as_str() {
|
||||
assert_eq!(AlkTypeKind::Uint32.as_str(), "AlkType:Uint32");
|
||||
assert_eq!(AlkTypeKind::Uint32.to_bast_str(), "uint32");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user