use std::{collections::HashMap, fmt};
use anyhow::anyhow;
use crate::entity_data_model_parse::edm;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct QualifiedName<'a> {
pub qualifier: &'a str,
pub uq_name: &'a str,
}
impl<'a> QualifiedName<'a> {
pub fn from_parsed(t_qualified_name: &'a edm::TQualifiedName) -> Result<Self, anyhow::Error> {
let raw = t_qualified_name.0.0.as_str();
match CategorisedIdentifier::from_raw(raw) {
CategorisedIdentifier::NamespaceOrQualifiedName(qualifier, uq_name) => {
Ok(QualifiedName { qualifier, uq_name })
}
_ => Err(anyhow!("Invalid qualified name: {}", raw)),
}
}
pub fn with_namespace(self, namespace_alias_lookup: &HashMap<&str, &'a str>) -> Self {
if let Some(ns) = namespace_alias_lookup.get(self.qualifier) {
QualifiedName {
qualifier: ns,
uq_name: self.uq_name,
}
} else {
self
}
}
pub fn with_path(self, path: &'a str) -> TargetPath<'a> {
let QualifiedName { qualifier, uq_name } = self;
TargetPath {
qualifier,
uq_name,
path,
}
}
}
impl<'a> fmt::Display for QualifiedName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{}.{}", self.qualifier, self.uq_name))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TargetPath<'a> {
pub qualifier: &'a str,
pub uq_name: &'a str,
pub path: &'a str,
}
impl<'a> TargetPath<'a> {
pub fn from_parsed(
t_path: &'a edm::TPath,
qualified_name: QualifiedName<'a>,
) -> Result<Self, anyhow::Error> {
let raw = t_path.0.0.as_str();
match CategorisedIdentifier::from_raw(raw) {
CategorisedIdentifier::SimpleIdentifer(path) => Ok(qualified_name.with_path(path)),
CategorisedIdentifier::UnqualifiedTargetPath(uq_name, path) => Ok(TargetPath {
qualifier: qualified_name.qualifier,
uq_name,
path,
}),
CategorisedIdentifier::QualifiedTargetPath(qualifier, uq_name, path) => {
Ok(TargetPath {
qualifier,
uq_name,
path,
})
}
_ => Err(anyhow!("Invalid target path: {}", raw)),
}
}
pub fn with_namespace(self, namespace_alias_lookup: &HashMap<&str, &'a str>) -> Self {
if let Some(ns) = namespace_alias_lookup.get(self.qualifier) {
TargetPath {
qualifier: ns,
uq_name: self.uq_name,
path: self.path,
}
} else {
self
}
}
pub fn parent_qualified_name(self) -> QualifiedName<'a> {
QualifiedName {
qualifier: self.qualifier,
uq_name: self.uq_name,
}
}
}
impl<'a> fmt::Display for TargetPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"{}.{}/{}",
self.qualifier, self.uq_name, self.path
))
}
}
enum CategorisedIdentifier<'a> {
SimpleIdentifer(&'a str),
NamespaceOrQualifiedName(&'a str, &'a str),
UnqualifiedTargetPath(&'a str, &'a str),
QualifiedTargetPath(&'a str, &'a str, &'a str),
Invalid,
}
impl<'a> CategorisedIdentifier<'a> {
fn from_raw(raw: &'a str) -> Self {
let (name, path) = match raw.find('/') {
Some(slash_start) => {
let slash_end = slash_start + unicode_char_size('/');
(&raw[..slash_start], Some(&raw[slash_end..]))
}
None => (raw, None),
};
if let Some(path) = path {
if path.contains('.') {
return CategorisedIdentifier::Invalid;
}
}
let (qualifier, unqualified_name) = match name.rfind('.') {
Some(dot_start) => {
let dot_end = dot_start + unicode_char_size('.');
(Some(&name[..dot_start]), &name[dot_end..])
}
None => (None, name),
};
match (qualifier, path) {
(None, None) => CategorisedIdentifier::SimpleIdentifer(unqualified_name),
(Some(qualifier), None) => {
CategorisedIdentifier::NamespaceOrQualifiedName(qualifier, unqualified_name)
}
(None, Some(path)) => CategorisedIdentifier::UnqualifiedTargetPath(name, path),
(Some(qualifier), Some(path)) => {
CategorisedIdentifier::QualifiedTargetPath(qualifier, unqualified_name, path)
}
}
}
}
fn unicode_char_size(c: char) -> usize {
let mut utf8_encoded = [0; 4];
c.encode_utf8(&mut utf8_encoded).len()
}