Skip to content

Commit

Permalink
refactor partially Errors, make creating relative paths easy
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivier committed Jan 11, 2025
1 parent 47edbf6 commit 5b2f57e
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 56 deletions.
30 changes: 28 additions & 2 deletions async-opcua-types/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,43 @@
use thiserror::Error;

use crate::VariantScalarTypeId;
use crate::{StatusCode, VariantScalarTypeId};

/// Rust OpcUa specific errors
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum OpcUAError {
pub enum OpcUaError {
#[error("Received an unexpected variant type")]
UnexpectedVariantType {
variant_id: Option<VariantScalarTypeId>,
message: String,
},
#[error("The requested namespace does not exists")]
NamespaceDoesNotExist(String),
#[error("Namespace is out of range of a u16.")]
NamespaceOutOfRange,
#[error("Supplied node resolver was unable to resolve a reference type.")]
UnresolvedReferenceType,
#[error("Path does not match a node.")]
NoMatch,
#[error("Path segment is unusually long and has been rejected.")]
PathSegmentTooLong,
#[error("Number of elements in relative path is too large.")]
TooManyElementsInPath,
#[error("Request returned a StatusCode Error.")]
StatusCodeError(StatusCode),
#[error("Generic Error.")]
Error(crate::Error),
}

impl From<StatusCode> for OpcUaError {
fn from(value: StatusCode) -> Self {
OpcUaError::StatusCodeError(value)
}
}

impl From<crate::Error> for OpcUaError {
fn from(value: crate::Error) -> Self {
OpcUaError::Error(value)
}
}
22 changes: 2 additions & 20 deletions async-opcua-types/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ use crate::{
AnonymousIdentityToken, ApplicationDescription, CallMethodRequest, DataTypeId,
EndpointDescription, Error, ExpandedNodeId, HistoryUpdateType, IdentityCriteriaType,
MessageSecurityMode, MonitoredItemCreateRequest, MonitoringMode, MonitoringParameters,
NumericRange, ObjectId, ReadValueId, ReferenceTypeId, RelativePath, RelativePathElement,
ServiceCounterDataType, ServiceFault, SignatureData, UserNameIdentityToken, UserTokenPolicy,
UserTokenType,
NumericRange, ObjectId, ReadValueId, ServiceCounterDataType, ServiceFault, SignatureData,
UserNameIdentityToken, UserTokenPolicy, UserTokenType,
};

use super::PerformUpdateType;
Expand Down Expand Up @@ -415,20 +414,3 @@ impl Default for IdentityCriteriaType {
Self::Anonymous
}
}

impl From<&[QualifiedName]> for RelativePath {
fn from(value: &[QualifiedName]) -> Self {
let elements = value
.iter()
.map(|qn| RelativePathElement {
reference_type_id: ReferenceTypeId::HierarchicalReferences.into(),
is_inverse: false,
include_subtypes: true,
target_name: qn.clone(),
})
.collect();
Self {
elements: Some(elements),
}
}
}
6 changes: 3 additions & 3 deletions async-opcua-types/src/namespaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use hashbrown::HashMap;

use crate::{errors::OpcUAError, ExpandedNodeId, NodeId, Variant};
use crate::{errors::OpcUaError, ExpandedNodeId, NodeId, Variant};

/// Utility for handling assignment of namespaces on server startup.
#[derive(Debug, Default, Clone)]
Expand All @@ -28,15 +28,15 @@ impl NamespaceMap {

/// Create a new namespace map from a vec of variant as we get when reading
/// the namespace array from the server
pub fn new_from_variant_array(array: &[Variant]) -> Result<Self, OpcUAError> {
pub fn new_from_variant_array(array: &[Variant]) -> Result<Self, OpcUaError> {
let known_namespaces: HashMap<String, u16> = array
.iter()
.enumerate()
.map(|(idx, v)| {
if let Variant::String(s) = v {
Ok((s.value().clone().unwrap_or(String::new()), idx as u16))
} else {
Err(OpcUAError::UnexpectedVariantType {
Err(OpcUaError::UnexpectedVariantType {
variant_id: v.scalar_type_id(),
message: "Namespace array on server contains invalid data".to_string(),
})
Expand Down
62 changes: 35 additions & 27 deletions async-opcua-types/src/relative_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use log::error;
use regex::Regex;

use crate::{
errors::OpcUaError,
node_id::{Identifier, NodeId},
qualified_name::QualifiedName,
string::UAString,
Expand All @@ -28,7 +29,7 @@ impl RelativePath {
/// Converts a string into a relative path. Caller must supply a `node_resolver` which will
/// be used to look up nodes from their browse name. The function will reject strings
/// that look unusually long or contain too many elements.
pub fn from_str<CB>(path: &str, node_resolver: &CB) -> Result<RelativePath, RelativePathError>
pub fn from_str<CB>(path: &str, node_resolver: &CB) -> Result<RelativePath, OpcUaError>
where
CB: Fn(u16, &str) -> Option<NodeId>,
{
Expand Down Expand Up @@ -66,14 +67,14 @@ impl RelativePath {
}
if token.len() > Self::MAX_TOKEN_LEN {
error!("Path segment seems unusually long and has been rejected");
return Err(RelativePathError::PathSegmentTooLong);
return Err(OpcUaError::PathSegmentTooLong);
}
}

if !token.is_empty() {
if elements.len() == Self::MAX_ELEMENTS {
error!("Number of elements in relative path is too long, rejecting it");
return Err(RelativePathError::TooManyElements);
return Err(OpcUaError::TooManyElementsInPath);
}
elements.push(RelativePathElement::from_str(&token, node_resolver)?);
}
Expand All @@ -84,6 +85,31 @@ impl RelativePath {
}
}

impl From<&[QualifiedName]> for RelativePath {
fn from(value: &[QualifiedName]) -> Self {
let elements = value
.iter()
.map(|qn| RelativePathElement {
reference_type_id: ReferenceTypeId::HierarchicalReferences.into(),
is_inverse: false,
include_subtypes: true,
target_name: qn.clone(),
})
.collect();
Self {
elements: Some(elements),
}
}
}

impl TryFrom<&str> for RelativePath {
type Error = OpcUaError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
RelativePath::from_str(value, &RelativePathElement::default_node_resolver)
}
}

impl<'a> From<&'a RelativePathElement> for String {
fn from(element: &'a RelativePathElement) -> String {
let mut result = element
Expand Down Expand Up @@ -226,10 +252,7 @@ impl RelativePathElement {
/// * `<!NonHierarchicalReferences>foo`
/// * `<#!2:MyReftype>2:blah`
///
pub fn from_str<CB>(
path: &str,
node_resolver: &CB,
) -> Result<RelativePathElement, RelativePathError>
pub fn from_str<CB>(path: &str, node_resolver: &CB) -> Result<RelativePathElement, OpcUaError>
where
CB: Fn(u16, &str) -> Option<NodeId>,
{
Expand Down Expand Up @@ -270,7 +293,7 @@ impl RelativePathElement {
node_resolver(namespace, browse_name)
} else {
error!("Namespace {} is out of range", namespace);
return Err(RelativePathError::NamespaceOutOfRange);
return Err(OpcUaError::NamespaceOutOfRange);
}
} else {
node_resolver(0, browse_name)
Expand All @@ -280,7 +303,7 @@ impl RelativePathElement {
"Supplied node resolver was unable to resolve a reference type from {}",
path
);
return Err(RelativePathError::UnresolvedReferenceType);
return Err(OpcUaError::UnresolvedReferenceType);
}
(reference_type_id.unwrap(), include_subtypes, is_inverse)
}
Expand All @@ -293,7 +316,7 @@ impl RelativePathElement {
})
} else {
error!("Path {} does not match a relative path", path);
Err(RelativePathError::NoMatch)
Err(OpcUaError::NoMatch)
}
}

Expand Down Expand Up @@ -340,21 +363,6 @@ impl RelativePathElement {
}
}

#[derive(Debug, Clone)]
/// Error returned from parsing a relative path.
pub enum RelativePathError {
/// Namespace is out of range of a u16.
NamespaceOutOfRange,
/// Supplied node resolver was unable to resolve a reference type.
UnresolvedReferenceType,
/// Path does not match a relative path.
NoMatch,
/// Path segment is unusually long and has been rejected.
PathSegmentTooLong,
/// Number of elements in relative path is too large.
TooManyElements,
}

impl<'a> From<&'a RelativePath> for String {
fn from(path: &'a RelativePath) -> String {
if let Some(ref elements) = path.elements {
Expand Down Expand Up @@ -398,7 +406,7 @@ fn unescape_browse_name(name: &str) -> String {
/// * 0:foo
/// * bar
///
fn target_name(target_name: &str) -> Result<QualifiedName, RelativePathError> {
fn target_name(target_name: &str) -> Result<QualifiedName, OpcUaError> {
static RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"((?P<nsidx>[0-9+]):)?(?P<name>.*)").unwrap());
if let Some(captures) = RE.captures(target_name) {
Expand All @@ -410,7 +418,7 @@ fn target_name(target_name: &str) -> Result<QualifiedName, RelativePathError> {
"Namespace {} for target name is out of range",
namespace.as_str()
);
return Err(RelativePathError::NamespaceOutOfRange);
return Err(OpcUaError::NamespaceOutOfRange);
}
} else {
0
Expand Down
9 changes: 5 additions & 4 deletions samples/custom-structures-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use opcua::{
crypto::SecurityPolicy,
types::{
custom::{DynamicStructure, DynamicTypeLoader},
BrowsePath, MessageSecurityMode, ObjectId, StatusCode, TimestampsToReturn, TypeLoader,
UserTokenPolicy, Variant,
errors::OpcUaError,
BrowsePath, MessageSecurityMode, ObjectId, TimestampsToReturn, TypeLoader, UserTokenPolicy,
Variant,
},
};

Expand Down Expand Up @@ -100,7 +101,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

async fn read_structure_var(session: &Arc<Session>, ns: u16) -> Result<(), StatusCode> {
async fn read_structure_var(session: &Arc<Session>, ns: u16) -> Result<(), OpcUaError> {
let type_tree = DataTypeTreeBuilder::new(|f| f.namespace <= ns)
.build(session)
.await
Expand All @@ -112,7 +113,7 @@ async fn read_structure_var(session: &Arc<Session>, ns: u16) -> Result<(), Statu
let res = session
.translate_browse_paths_to_node_ids(&[BrowsePath {
starting_node: ObjectId::ObjectsFolder.into(),
relative_path: (&["ErrorData".into()][..]).into(),
relative_path: "/0:ErrorData".try_into()?,
}])
.await?;
let Some(target) = &res[0].targets else {
Expand Down

0 comments on commit 5b2f57e

Please sign in to comment.