[package]
name = "epics-ca-rs"
version.workspace = true
edition.workspace = true
description = "EPICS Channel Access protocol client and server"
license-file = "LICENSE"
authors.workspace = true
repository.workspace = true

[dependencies]
epics-base-rs = { workspace = true }
tokio = { version = "1", features = ["full"] }
chrono = "0.4"
clap = { version = "4", features = ["derive"] }
socket2 = { version = "0.5", features = ["all"] }
if-addrs = "0.13"
libc = "0.2"
tracing = "0.1"
metrics = "0.23"
async-trait = "0.1"
futures-util = "0.3"
dashmap = "6"
parking_lot = "0.12"
# Lock-free snapshot cell for the `calink` CA-link monitor caches.
arc-swap = "1"
metrics-exporter-prometheus = { version = "0.15", default-features = false, features = ["http-listener"], optional = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"], optional = true }
rustls-pemfile = { version = "2", optional = true }
rustls-pki-types = { version = "1", optional = true }
x509-parser = { version = "0.16", optional = true }
sha2 = { version = "0.10", optional = true }
mdns-sd = { version = "0.13", default-features = false, features = ["async"], optional = true }
hickory-resolver = { version = "0.24", default-features = false, features = ["tokio-runtime", "system-config"], optional = true }
hickory-client = { version = "0.24", default-features = false, features = ["dnssec-ring"], optional = true }
hickory-proto = { version = "0.24", default-features = false, features = ["dnssec-ring", "tokio-runtime"], optional = true }
base64 = { version = "0.22", optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
serde_json = { version = "1", optional = true }
ed25519-dalek = { version = "2", optional = true, features = ["rand_core"] }
rand_core = { version = "0.6", optional = true, features = ["getrandom"] }
# Non-optional: the always-on `calink` module derives `thiserror::Error`
# for `CaLinkError`. cap-tokens / signed_beacon also use it.
thiserror = "1"
opentelemetry = { version = "0.24", optional = true }
opentelemetry_sdk = { version = "0.24", default-features = false, features = ["trace", "rt-tokio"], optional = true }
opentelemetry-otlp = { version = "0.17", default-features = false, features = ["grpc-tonic", "trace"], optional = true }
tracing-opentelemetry = { version = "0.25", optional = true }

[features]
default = []
# Bundled Prometheus exporter + tracing-subscriber init helpers.
# Without this feature consumers wire up their own observability
# stack via the bare `metrics` and `tracing` facades.
observability = ["dep:metrics-exporter-prometheus", "dep:tracing-subscriber"]
# EXPERIMENTAL Rust-only CA-over-TLS — NOT part of the EPICS spec.
#
# Enabling this and configuring TLS makes the IOC unreachable from
# C tools (caget, caput, camonitor, EDM, MEDM, CSS) and pyepics, since
# libca/rsrv have no TLS support. For production traffic encryption,
# prefer network-layer solutions (IPSec, WireGuard, site VPN) — they
# protect all EPICS traffic transparently and don't break interop.
#
# This feature exists for Rust-only deployments and as a foundation
# for future epics-base 7 `ca-secure` wire compatibility (NOT yet
# implemented; the wire format is currently Rust-only).
#
# See doc/11-tls-design.md for rationale and migration guidance.
experimental-rust-tls = [
    "dep:tokio-rustls",
    "dep:rustls-pemfile",
    "dep:rustls-pki-types",
    "dep:x509-parser",
    "dep:sha2",
]
# mDNS + DNS-SD service discovery. Adds mdns-sd (link-local
# multicast) and hickory-resolver (unicast DNS PTR/SRV/TXT lookups).
# Server can announce itself via mDNS; client can auto-discover
# IOCs via mDNS and/or DNS-SD against an existing site DNS server.
# See doc/12-discovery.md.
discovery = ["dep:mdns-sd", "dep:hickory-resolver"]
# Dynamic DNS UPDATE (RFC 2136) for self-registering IOCs against a
# unicast DNS server. TSIG (RFC 2845) signing supported via
# HMAC-SHA256 / HMAC-SHA512. Adds hickory-client + base64.
discovery-dns-update = [
    "discovery",
    "dep:hickory-client",
    "dep:hickory-proto",
    "dep:base64",
]
# OpenTelemetry OTLP trace exporter. Wires the `tracing` ecosystem
# into a remote collector (Tempo, Jaeger, OTel Collector) over gRPC.
# Pulls in the opentelemetry-otlp + tracing-opentelemetry dep tree —
# leave off unless you actually need remote tracing.
otlp = [
    "observability",
    "dep:opentelemetry",
    "dep:opentelemetry_sdk",
    "dep:opentelemetry-otlp",
    "dep:tracing-opentelemetry",
]
# Ed25519 capability tokens for identity-based access control. Used
# as an alternative to UNIX-style host/user matching for clients
# behind NAT, in containers, or otherwise unable to present a stable
# host identity. Adds ed25519-dalek + rand_core + base64.
cap-tokens = [
    "dep:ed25519-dalek",
    "dep:rand_core",
    "dep:base64",
    "dep:serde",
    "dep:serde_json",
    "dep:sha2",
]

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
tempfile = "3"
proptest = "1"
rcgen = "0.13"
serial_test = "3"
# test-util is required for `tokio::test(start_paused = true)` and
# `tokio::time::pause`, used by the lazy-echo virtual-clock tests in
# client::transport. Pulled in only for tests so production builds
# don't carry the test runtime hooks.
tokio = { version = "1", features = ["test-util"] }

[[bench]]
name = "protocol_bench"
harness = false

[[bench]]
name = "end_to_end_bench"
harness = false

[[bin]]
name = "caget-rs"
path = "src/bin/caget-rs.rs"

[[bin]]
name = "caput-rs"
path = "src/bin/caput-rs.rs"

[[bin]]
name = "camonitor-rs"
path = "src/bin/camonitor-rs.rs"

[[bin]]
name = "cainfo-rs"
path = "src/bin/cainfo-rs.rs"

[[bin]]
name = "ca-repeater-rs"
path = "src/bin/ca-repeater-rs.rs"

[[bin]]
name = "softioc-rs"
path = "src/bin/softioc-rs.rs"

[[bin]]
name = "ca-replay-rs"
path = "src/bin/ca-replay-rs.rs"

[[bin]]
name = "ca-lint-rs"
path = "src/bin/ca-lint-rs.rs"

[[bin]]
name = "ca-admin-rs"
path = "src/bin/ca-admin-rs.rs"

[[bin]]
name = "ca-soak"
path = "src/bin/ca-soak.rs"

[[bin]]
name = "ca-soak-observed"
path = "src/bin/ca-soak-observed.rs"
required-features = ["observability"]
