use std::collections::HashMap;
use crate::parsers::SyntaxKind;
use serde::Serialize;
#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, Copy, Hash)]
pub struct FileFormatVersion {
pub major: u8,
pub minor: u8,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Default, Copy, Hash)]
pub enum NameSpace {
Global,
#[default]
Local,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Default, Copy, Hash)]
pub enum Creatable {
False,
#[default]
True,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Default, Copy, Hash)]
pub enum PreDeclaredID {
#[default]
False,
True,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Default, Copy, Hash)]
pub enum Exposed {
#[default]
False,
True,
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct FileAttributes {
pub name: String, pub global_name_space: NameSpace, pub creatable: Creatable, pub predeclared_id: PreDeclaredID, pub exposed: Exposed, pub description: Option<String>, pub ext_key: HashMap<String, String>, }
impl Default for FileAttributes {
fn default() -> Self {
FileAttributes {
name: String::new(),
global_name_space: NameSpace::Local,
creatable: Creatable::True,
predeclared_id: PreDeclaredID::False,
exposed: Exposed::False,
description: None,
ext_key: HashMap::new(),
}
}
}
pub(crate) fn extract_version(
cst: &crate::parsers::ConcreteSyntaxTree,
) -> Option<FileFormatVersion> {
let version_nodes: Vec<_> = cst.children_by_kind(SyntaxKind::VersionStatement).collect();
if version_nodes.is_empty() {
return None;
}
let version_node = &version_nodes[0];
let mut found_version_keyword = false;
for child in version_node.children() {
if !found_version_keyword {
if child.kind() == SyntaxKind::VersionKeyword {
found_version_keyword = true;
}
continue;
}
if child.kind() == SyntaxKind::SingleLiteral {
let version_str = child.text().trim();
let parts: Vec<&str> = version_str.split('.').collect();
if parts.len() != 2 {
return None;
}
let major = parts[0].parse::<u8>().ok()?;
let minor = parts[1].parse::<u8>().ok()?;
return Some(FileFormatVersion { major, minor });
}
}
None
}
pub(crate) fn extract_attributes(cst: &crate::parsers::ConcreteSyntaxTree) -> FileAttributes {
use crate::files::common::{Creatable, Exposed, NameSpace, PreDeclaredID};
let mut name = String::new();
let mut global_name_space = NameSpace::Local;
let mut creatable = Creatable::True;
let mut predeclared_id = PreDeclaredID::False;
let mut exposed = Exposed::False;
let mut description: Option<String> = None;
let mut ext_key: HashMap<String, String> = HashMap::new();
let attr_statements: Vec<_> = cst
.children()
.into_iter()
.filter(|c| c.kind() == SyntaxKind::AttributeStatement)
.collect();
for attr_stmt in attr_statements {
let mut key = String::new();
let mut value = String::new();
let mut found_equals = false;
for child in attr_stmt.children() {
if !child.is_token() {
continue; }
match child.kind() {
SyntaxKind::Identifier => {
if !found_equals {
key = child.text().trim().to_string();
}
}
SyntaxKind::EqualityOperator => {
found_equals = true;
}
SyntaxKind::StringLiteral => {
if found_equals {
value = child.text().trim().trim_matches('"').to_string();
}
}
SyntaxKind::TrueKeyword => {
if found_equals {
value = "True".to_string();
}
}
SyntaxKind::FalseKeyword => {
if found_equals {
value = "False".to_string();
}
}
SyntaxKind::IntegerLiteral | SyntaxKind::LongLiteral => {
if found_equals {
value = child.text().trim().to_string();
}
}
SyntaxKind::SubtractionOperator => {
if found_equals && value.is_empty() {
value.push('-');
}
}
_ => {}
}
}
if !key.is_empty() {
match key.as_str() {
"VB_Name" => {
name = value;
}
"VB_GlobalNameSpace" => {
global_name_space = if value == "True" || value == "-1" {
NameSpace::Global
} else {
NameSpace::Local
};
}
"VB_Creatable" => {
creatable = if value == "True" || value == "-1" {
Creatable::True
} else {
Creatable::False
};
}
"VB_PredeclaredId" => {
predeclared_id = if value == "True" || value == "-1" {
PreDeclaredID::True
} else {
PreDeclaredID::False
};
}
"VB_Exposed" => {
exposed = if value == "True" || value == "-1" {
Exposed::True
} else {
Exposed::False
};
}
"VB_Description" => {
description = Some(value);
}
_ => {
ext_key.insert(key, value);
}
}
}
}
FileAttributes {
name,
global_name_space,
creatable,
predeclared_id,
exposed,
description,
ext_key,
}
}