use super::connect_options::Address;
use super::connect_options::AddressList;
use super::connect_options::Description;
use super::connect_options::DescriptionList;
use super::tnsnames_file_parser::lookup_tns_alias;
use crate::constants;
use crate::error::Error;
use crate::parser::Parser;
const NETWORK_NAME_SPECIAL_CHARS: [char; 21] = [
'"', '\'', '<', '>', '/', '\\', ',', '.', ':', ';', '-', '_', '$', '+',
'*', '#', '&', '!', '%', '?', '@',
];
fn is_host_name_char(ch: char) -> bool {
ch.is_alphabetic()
|| ch.is_ascii_digit()
|| ch == '-'
|| ch == '_'
|| ch == '.'
}
fn is_network_keyword_char(ch: char) -> bool {
ch.is_ascii_alphanumeric() || ch == '_' || ch == '.'
}
fn parse_descriptor_key(parser: &mut Parser) -> Result<String, Error> {
if let Some(token) = parser.parse_token(is_network_keyword_char) {
Ok(token.to_lowercase())
} else {
Err(parser.parse_error())
}
}
fn parse_descriptor_key_value_pair(
parser: &mut Parser,
) -> Result<Node, Error> {
parser.skip_whitespace();
let key = parse_descriptor_key(parser)?;
parser.skip_whitespace();
if !parser.next_char_matches('=') {
return Err(parser.parse_error());
}
parser.skip_whitespace();
let value = parse_descriptor_value(parser)?;
Ok(Node { key, value })
}
fn parse_descriptor_value(parser: &mut Parser) -> Result<NodeValue, Error> {
let value = if let Some(value) = parser.parse_delimited_text('"', '"') {
parser.skip_whitespace();
NodeValue::Simple(value)
} else if !parser.peek_char_matches('(') {
let token = parser.parse_token(|ch| ch != ')');
let value = token.unwrap_or_default().trim().to_string();
NodeValue::Simple(value)
} else {
let mut child_nodes = Vec::<Node>::new();
while !parser.peek_char_matches(')') && parser.next_char_matches('(') {
let node = parse_descriptor_key_value_pair(parser)?;
child_nodes.push(node);
parser.skip_whitespace();
}
NodeValue::Container(child_nodes)
};
if parser.next_char_matches(')') {
Ok(value)
} else {
Err(parser.parse_error())
}
}
fn parse_easy_connect_host(parser: &mut Parser) -> Option<String> {
let mut result = parser.parse_delimited_text('[', ']');
if result.is_none() {
result = parser.parse_token(is_host_name_char);
}
result
}
fn parse_easy_connect_hosts(
parser: &mut Parser,
protocol: Option<String>,
) -> Option<Description> {
let mut port_index = 0;
let mut description = Description::new();
let mut address_list = AddressList::new();
loop {
let host = parse_easy_connect_host(parser);
if host.is_none() {
break;
}
address_list
.addresses
.push(Address::new(host, protocol.clone()));
if let Some(ch) = parser.peek_char()
&& ch == ':'
{
parser.next_char();
let port = parse_easy_connect_port(parser);
if port.is_none() {
break;
}
for address in &mut address_list.addresses[port_index..] {
address.set_port(port);
}
port_index = address_list.addresses.len();
}
if let Some(ch) = parser.peek_char() {
if ch == ';' {
description.address_lists.push(address_list);
address_list = AddressList::new();
port_index = 0;
} else if ch != ',' {
description.address_lists.push(address_list);
return Some(description);
}
}
}
None
}
fn parse_easy_connect_instance_name(parser: &mut Parser) -> Option<String> {
parser.parse_token_with_sep(is_network_keyword_char, '/')
}
fn parse_easy_connect_port(parser: &mut Parser) -> Option<u16> {
let mut num_chars = 0;
while let Some(ch) = parser.peek_char() {
if !ch.is_ascii_digit() {
break;
}
parser.next_char();
num_chars += 1;
}
if num_chars > 0 {
let text = &parser.save_pos()[1..];
text.parse::<u16>().ok()
} else {
parser.restore_pos();
None
}
}
fn parse_easy_connect_protocol(parser: &mut Parser) -> Option<String> {
let mut num_sep_chars = 0;
let mut num_protocol_chars = 0;
let mut found_colon = false;
while let Some(ch) = parser.next_char() {
if ch == ':' && !found_colon && num_protocol_chars > 0 {
found_colon = true;
} else if ch == '/' && (found_colon || num_protocol_chars == 0) {
num_sep_chars += 1;
if num_sep_chars == 2 {
let text = &parser.save_pos()[..num_protocol_chars];
if text.is_empty() {
return None;
} else {
return Some(text.into());
}
}
} else if !found_colon
&& num_sep_chars == 0
&& (ch.is_alphabetic() || ch == '-' || ch == '_')
{
num_protocol_chars += 1;
} else {
break;
}
}
parser.restore_pos();
None
}
fn parse_easy_connect_server_type(parser: &mut Parser) -> Option<String> {
parser.parse_token_with_sep(|ch| ch.is_ascii_alphabetic(), ':')
}
fn parse_easy_connect_service_name(parser: &mut Parser) -> Option<String> {
parser.parse_token_with_sep(is_network_keyword_char, '/')
}
fn parse_easy_connect_string(parser: &mut Parser) -> Option<DescriptionList> {
let protocol = parse_easy_connect_protocol(parser);
match parse_easy_connect_hosts(parser, protocol) {
Some(mut description) => {
let service_name = parse_easy_connect_service_name(parser)?;
description.set_service_name(Some(service_name));
description
.set_server_type(parse_easy_connect_server_type(parser));
description
.set_instance_name(parse_easy_connect_instance_name(parser));
let mut description_list = DescriptionList::new();
description_list.descriptions.push(description);
Some(description_list)
}
None => None,
}
}
fn parse_full_descriptor(
parser: &mut Parser,
) -> Result<DescriptionList, Error> {
parser.next_char();
let top_node = parse_descriptor_key_value_pair(parser)?;
if top_node.key == "description_list" {
let description_list = DescriptionList::new_from_node(&top_node)?;
Ok(description_list)
} else if top_node.key == "description" {
let description = Description::new_from_node(&top_node)?;
let mut description_list = DescriptionList::new();
description_list.descriptions.push(description);
Ok(description_list)
} else {
Err(Error::invalid_connect_string(parser.source().to_string()))
}
}
pub(crate) fn is_network_name_char(ch: char) -> bool {
ch.is_ascii_alphanumeric() || NETWORK_NAME_SPECIAL_CHARS.contains(&ch)
}
pub(crate) fn parse_connect_string(
connect_string: &str,
) -> Result<Option<DescriptionList>, Error> {
let mut parser = Parser::new(connect_string.trim());
if parser.peek_char_matches('(') {
let description_list = parse_full_descriptor(&mut parser)?;
Ok(Some(description_list))
} else {
Ok(parse_easy_connect_string(&mut parser))
}
}
pub(crate) fn parse_connect_string_or_lookup_alias(
connect_string: &str,
config_dir_opt: &Option<String>,
) -> Result<DescriptionList, Error> {
match parse_connect_string(connect_string)? {
Some(description_list) => Ok(description_list),
None => match config_dir_opt {
Some(config_dir) => lookup_tns_alias(config_dir, connect_string),
None => Err(Error::no_config_dir()),
},
}
}
pub(crate) fn sanitize_network_name(value: &str) -> String {
let trimmed_value = value.trim_matches(['"', '\'']);
let mut output: String = trimmed_value
.chars()
.map(|c| if is_network_name_char(c) { c } else { '?' })
.collect();
if let Some(ch) = output.chars().last()
&& ch == '\\'
{
output.pop();
output.push('?');
}
output
}
pub(crate) fn validate_network_name(value: &str) -> Result<(), Error> {
let sanitized = sanitize_network_name(value);
if sanitized == value {
Ok(())
} else {
Err(Error::invalid_network_name(value.into()))
}
}
enum NodeValue {
Simple(String),
Container(Vec<Node>),
}
pub(crate) struct Node {
key: String,
value: NodeValue,
}
impl Node {
fn as_simple_value(&self, expected_type: &str) -> Result<&str, Error> {
match &self.value {
NodeValue::Simple(value) => Ok(value),
NodeValue::Container(_) => Err(self.invalid_value(expected_type)),
}
}
fn has_value(&self) -> bool {
match &self.value {
NodeValue::Simple(value) => !value.is_empty(),
NodeValue::Container(value) => !value.is_empty(),
}
}
fn invalid_value(&self, expected_type: &str) -> Error {
Error::invalid_descriptor_node(self.key.clone(), expected_type.into())
}
pub(crate) fn as_bool(&self) -> Result<bool, Error> {
let text_value = self.as_simple_value("bool")?;
Ok(matches!(
text_value.trim().to_lowercase().as_str(),
"on" | "yes" | "true"
))
}
pub(crate) fn as_purity(&self) -> Result<u8, Error> {
let text_value = self.as_simple_value("purity")?;
match text_value.to_lowercase().as_str() {
"new" => Ok(constants::PURITY_NEW),
"self" => Ok(constants::PURITY_SELF),
_ => Err(self.invalid_value("purity")),
}
}
pub(crate) fn as_server_type(&self) -> Result<String, Error> {
let value = self.as_simple_value("server_type")?;
match value.to_lowercase().as_str() {
"dedicated" | "pooled" | "shared" => Ok(value.to_string()),
_ => Err(self.invalid_value("server_type")),
}
}
pub(crate) fn as_str(&self) -> Result<String, Error> {
let value = self.as_simple_value("String")?;
Ok(value.to_string())
}
pub(crate) fn as_u16(&self) -> Result<u16, Error> {
let text_value = self.as_simple_value("u16")?;
match text_value.parse::<u16>() {
Ok(value) => Ok(value),
Err(_) => Err(self.invalid_value("u16")),
}
}
pub(crate) fn as_u32(&self) -> Result<u32, Error> {
let text_value = self.as_simple_value("u32")?;
match text_value.parse::<u32>() {
Ok(value) => Ok(value),
Err(_) => Err(self.invalid_value("u32")),
}
}
pub(crate) fn as_u64(&self) -> Result<u64, Error> {
let text_value = self.as_simple_value("u64")?;
match text_value.parse::<u64>() {
Ok(value) => Ok(value),
Err(_) => Err(self.invalid_value("u64")),
}
}
pub(crate) fn key(&self) -> &str {
self.key.as_str()
}
pub(crate) fn process_child_nodes(
&self,
mut f: impl FnMut(&Node) -> Result<(), Error>,
) -> Result<(), Error> {
match &self.value {
NodeValue::Simple(_) => {}
NodeValue::Container(child_nodes) => {
for child_node in child_nodes {
if child_node.has_value() {
f(child_node)?;
}
}
}
}
Ok(())
}
}