use opcua_types::{
BrowseDescription, BrowseDirection, BrowseResultMaskFlags, ByteString, NodeClassMask, NodeId,
ReferenceDescription, ReferenceTypeId, StatusCode,
};
use tokio_util::sync::CancellationToken;
mod browse;
mod result;
pub use result::{BrowserResult, NodeDescription};
use crate::{RequestRetryPolicy, Session};
#[derive(Debug, Clone)]
pub struct BrowserConfig {
max_nodes_per_request: usize,
max_references_per_node: u32,
max_concurrent_requests: usize,
max_continuation_point_retries: usize,
}
impl BrowserConfig {
pub fn new() -> Self {
Self::default()
}
pub fn max_nodes_per_request(mut self, max_nodes_per_request: usize) -> Self {
self.max_nodes_per_request = max_nodes_per_request;
self
}
pub fn max_references_per_node(mut self, max_references_per_node: u32) -> Self {
self.max_references_per_node = max_references_per_node;
self
}
pub fn max_concurrent_requests(mut self, max_concurrent_requests: usize) -> Self {
self.max_concurrent_requests = max_concurrent_requests;
self
}
pub fn max_continuation_point_retries(mut self, max_continuation_point_retries: usize) -> Self {
self.max_continuation_point_retries = max_continuation_point_retries;
self
}
}
impl Default for BrowserConfig {
fn default() -> Self {
Self {
max_nodes_per_request: 100,
max_references_per_node: 1000,
max_concurrent_requests: 1,
max_continuation_point_retries: 0,
}
}
}
#[derive(Debug, Default, Clone)]
struct RequestWithRetries {
pub(self) request: BrowseDescription,
pub(self) num_outer_retries: usize,
pub(self) depth: usize,
}
#[derive(Debug, Default)]
pub struct BrowseResultItem {
pub(self) request: RequestWithRetries,
pub(self) references: Vec<ReferenceDescription>,
pub(self) status: StatusCode,
pub(self) request_continuation_point: Option<ByteString>,
}
impl BrowseResultItem {
pub fn parent_id(&self) -> &NodeId {
&self.request.request.node_id
}
pub fn into_results(self) -> (NodeId, Vec<ReferenceDescription>) {
(self.request.request.node_id, self.references)
}
pub fn references(&self) -> &[ReferenceDescription] {
&self.references
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn request(&self) -> &BrowseDescription {
&self.request.request
}
pub fn is_browse_next(&self) -> bool {
self.request_continuation_point.is_some()
}
pub fn depth(&self) -> usize {
self.request.depth + 1
}
}
pub trait BrowserPolicy {
fn get_next(&self, results: &BrowseResultItem) -> Vec<BrowseDescription>;
}
impl<T> BrowserPolicy for T
where
T: for<'a> Fn(&BrowseResultItem) -> Vec<BrowseDescription> + Send + Sync,
{
fn get_next(&self, results: &BrowseResultItem) -> Vec<BrowseDescription> {
self(results)
}
}
#[derive(Debug, Clone, Copy)]
pub struct NoneBrowserPolicy;
impl BrowserPolicy for NoneBrowserPolicy {
fn get_next(&self, _results: &BrowseResultItem) -> Vec<BrowseDescription> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct BrowseFilter {
direction: BrowseDirection,
include_subtypes: bool,
result_mask: BrowseResultMaskFlags,
node_class_mask: NodeClassMask,
reference_type_id: NodeId,
max_depth: usize,
}
impl BrowserPolicy for BrowseFilter {
fn get_next(&self, results: &BrowseResultItem) -> Vec<BrowseDescription> {
if self.max_depth > 0 && results.depth() >= self.max_depth {
return Vec::new();
}
results
.references
.iter()
.filter(|r| r.node_id.server_index == 0)
.map(|r| self.new_description_from_node(r.node_id.node_id.clone()))
.collect()
}
}
impl BrowseFilter {
pub fn new(
direction: BrowseDirection,
reference_type_id: impl Into<NodeId>,
include_subtypes: bool,
) -> Self {
Self {
direction,
reference_type_id: reference_type_id.into(),
include_subtypes,
result_mask: BrowseResultMaskFlags::all(),
node_class_mask: NodeClassMask::all(),
max_depth: 0,
}
}
pub fn new_description_from_node(&self, node_id: NodeId) -> BrowseDescription {
BrowseDescription {
node_id,
browse_direction: self.direction,
reference_type_id: self.reference_type_id.clone(),
include_subtypes: self.include_subtypes,
node_class_mask: self.node_class_mask.bits(),
result_mask: self.result_mask.bits(),
}
}
pub fn new_hierarchical() -> Self {
Self::new(
BrowseDirection::Forward,
ReferenceTypeId::HierarchicalReferences,
true,
)
}
pub fn node_class_mask(mut self, mask: NodeClassMask) -> Self {
self.node_class_mask = mask;
self
}
pub fn result_mask(mut self, mask: BrowseResultMaskFlags) -> Self {
self.result_mask = mask;
self
}
pub fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
}
pub struct Browser<'a, T, R> {
pub(self) handler: T,
pub(self) retry_policy: R,
pub(self) config: BrowserConfig,
pub(self) session: &'a Session,
pub(self) token: CancellationToken,
}
impl<'a, T, R> Browser<'a, T, R> {
pub fn new(session: &'a Session, handler: T, retry_policy: R) -> Self {
Self {
session,
handler,
retry_policy,
config: BrowserConfig::default(),
token: CancellationToken::new(),
}
}
pub fn handler<T2: BrowserPolicy + 'a>(self, new_handler: T2) -> Browser<'a, T2, R> {
Browser {
handler: new_handler,
retry_policy: self.retry_policy,
config: self.config,
session: self.session,
token: self.token,
}
}
pub fn retry_policy<R2: RequestRetryPolicy + Clone + 'a>(
self,
new_retry_policy: R2,
) -> Browser<'a, T, R2> {
Browser {
handler: self.handler,
retry_policy: new_retry_policy,
config: self.config,
session: self.session,
token: self.token,
}
}
pub fn token(mut self, token: CancellationToken) -> Self {
self.token = token;
self
}
pub fn max_nodes_per_request(mut self, max_nodes_per_request: usize) -> Self {
self.config.max_nodes_per_request = max_nodes_per_request;
self
}
pub fn max_references_per_node(mut self, max_references_per_node: u32) -> Self {
self.config.max_references_per_node = max_references_per_node;
self
}
pub fn max_concurrent_requests(mut self, max_concurrent_requests: usize) -> Self {
self.config.max_concurrent_requests = max_concurrent_requests;
self
}
pub fn max_continuation_point_retries(mut self, max_continuation_point_retries: usize) -> Self {
self.config.max_continuation_point_retries = max_continuation_point_retries;
self
}
pub fn config(mut self, config: BrowserConfig) -> Self {
self.config = config;
self
}
}