use std::sync::Arc;
use arrow::datatypes::{DataType, Field, Schema, TimeUnit};
pub const PARSER_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const SCHEMA_MAJOR: u32 = 0;
pub const SCHEMA_MINOR: u32 = 1;
#[must_use]
pub fn resources_schema() -> Schema {
Schema::new(vec![
utf8_field("workspace_root"),
utf8_field("component_path"),
utf8_field("module_path"),
utf8_field("address"),
utf8_field("kind"),
utf8_field("resource_type"),
utf8_field("resource_name"),
utf8_field("provider_local"),
utf8_field("provider_source"),
utf8_field("account_id"),
utf8_field("account_name"),
utf8_field("region"),
utf8_field("environment"),
utf8_field("count_expr"),
utf8_field("for_each_expr"),
Field::new(
"depends_on",
DataType::List(Arc::new(Field::new("item", DataType::Utf8, false))),
false,
),
utf8_field("attributes_json"),
utf8_field("state_account_id"),
utf8_field("state_region"),
utf8_field("file"),
Field::new("line", DataType::UInt32, false),
Field::new("column", DataType::UInt32, false),
utf8_field("parser_version"),
Field::new(
"parsed_at",
DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from("UTC"))),
false,
),
])
}
#[must_use]
pub fn schema_field_names() -> Vec<&'static str> {
vec![
"workspace_root",
"component_path",
"module_path",
"address",
"kind",
"resource_type",
"resource_name",
"provider_local",
"provider_source",
"account_id",
"account_name",
"region",
"environment",
"count_expr",
"for_each_expr",
"depends_on",
"attributes_json",
"state_account_id",
"state_region",
"file",
"line",
"column",
"parser_version",
"parsed_at",
]
}
fn utf8_field(name: &'static str) -> Field {
Field::new(name, DataType::Utf8, false)
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)]
mod tests {
use super::*;
#[test]
fn test_schema_has_24_columns() {
let s = resources_schema();
assert_eq!(s.fields().len(), 24);
}
#[test]
fn test_schema_columns_match_documented_names() {
let s = resources_schema();
let got: Vec<&str> = s.fields().iter().map(|f| f.name().as_str()).collect();
assert_eq!(got, schema_field_names());
}
#[test]
fn test_depends_on_is_non_null_list_of_utf8() {
let s = resources_schema();
let depends_on = s.field_with_name("depends_on").unwrap();
assert!(!depends_on.is_nullable());
match depends_on.data_type() {
DataType::List(item) => {
assert_eq!(item.data_type(), &DataType::Utf8);
assert!(!item.is_nullable());
}
other => panic!("expected List<Utf8>, got {other:?}"),
}
}
#[test]
fn test_parsed_at_is_timestamp_ms_utc() {
let s = resources_schema();
let f = s.field_with_name("parsed_at").unwrap();
match f.data_type() {
DataType::Timestamp(TimeUnit::Millisecond, tz) => {
assert_eq!(tz.as_deref(), Some("UTC"));
}
other => panic!("expected Timestamp(ms, UTC), got {other:?}"),
}
}
#[test]
fn test_no_nullable_columns() {
let s = resources_schema();
for f in s.fields() {
assert!(!f.is_nullable(), "column `{}` is nullable", f.name());
}
}
#[test]
fn test_field_names_match_schema_size() {
let s = resources_schema();
assert_eq!(s.fields().len(), schema_field_names().len());
}
}