6 releases
Uses new Rust 2024
| 0.1.6 | Jan 6, 2026 |
|---|---|
| 0.1.5 | Jan 6, 2026 |
| 0.1.4 | Dec 29, 2025 |
#625 in Network programming
800KB
19K
SLoC
πΊπΈ English Β· π¨π³ δΈζ | Table of Contents
svn-rs
A Rust async client for Subversion svn:// (ra_svn) β a modern alternative to libsvn_ra_svn.
Features
- Async-first
svn://(ra_svn) client (no working copy). - Optional
svn+ssh://via SSH tunnel (sshfeature; runssvnserve -tover SSH). - High-level API:
RaSvnClient/RaSvnSession. - Structured server errors (
code/message/file/line) with command context. serdefeature for public data types.- Optional
cyrus-saslfeature for Cyrus SASL auth + negotiated SASL security layer (requires a system-providedlibsasl2at runtime).
Installation
Add the crate:
cargo add svn
Or in Cargo.toml:
[dependencies]
svn = "0.1"
You also need an async runtime. The examples below use tokio:
[dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
Quick start
The RaSvnClient type is a reusable configuration; use it to create a connected
RaSvnSession (which owns a single TCP connection).
use std::time::Duration;
use svn::{RaSvnClient, SvnUrl};
#[tokio::main]
async fn main() -> svn::Result<()> {
let url = SvnUrl::parse("svn://example.com/repo")?;
let client = RaSvnClient::new(url, None, None)
.with_connect_timeout(Duration::from_secs(10))
.with_read_timeout(Duration::from_secs(30))
.with_write_timeout(Duration::from_secs(30));
let mut session = client.open_session().await?;
let latest = session.get_latest_rev().await?;
println!("HEAD = {latest}");
Ok(())
}
Configuration
RaSvnClient is cheap to clone and provides builder-style methods:
with_connect_timeout(Duration)with_read_timeout(Duration)with_write_timeout(Duration)with_ra_client(String)(sent during handshake)
In general, prefer reusing a single RaSvnSession for multiple operations to
avoid repeated reconnect + handshake.
Authentication
This crate supports svn:// authentication mechanisms commonly offered by
svnserve:
ANONYMOUSPLAIN(username + password)CRAM-MD5(username + password)
Pass username/password when creating RaSvnClient. If the server requires an
unsupported mechanism, operations return SvnError::AuthUnavailable.
To enable Cyrus SASL (and the optional SASL security layer), enable the
cyrus-sasl feature (requires libsasl2 installed on the system at runtime):
svn = { version = "0.1", features = ["cyrus-sasl"] }
Notes:
- With
cyrus-sasl, this crate dynamically loads the system Cyrus SASL library (libsasl2) at runtime. If it is not available, SASL authentication is unavailable and requests may fail withSvnError::AuthUnavailable. - The SASL security layer is not TLS (no certificates); it is an optional integrity/encryption layer negotiated as part of SASL, depending on the mechanism and server configuration.
- When
cyrus-saslis disabled (the default), the crate staysunsafe-free.
svn+ssh:// (SSH tunnel)
Enable the ssh feature to connect using svn+ssh:// URLs (runs svnserve -t
over SSH using russh):
svn = { version = "0.1", features = ["ssh"] }
use std::path::PathBuf;
use svn::{RaSvnClient, SshAuth, SshConfig, SvnUrl};
# #[tokio::main] async fn main() -> svn::Result<()> {
let url = SvnUrl::parse("svn+ssh://example.com/repo")?;
let ssh = SshConfig::new(SshAuth::KeyFile {
path: PathBuf::from("~/.ssh/id_ed25519"),
passphrase: None,
});
let client = RaSvnClient::new(url, None, None).with_ssh_config(ssh);
let mut session = client.open_session().await?;
let head = session.get_latest_rev().await?;
println!("{head}");
# Ok(()) }
Supported operations
This crate focuses on ra_svn protocol v2 and currently supports:
- Read:
get-latest-rev,get-dated-rev,get-file,get-dir,log,list,check-path,stat,get-mergeinfo,get-deleted-rev,get-locations,get-location-segments,get-file-revs,rev-prop,rev-proplist,proplist,propget,get-iprops, locks listing (get-lock,get-locks). - Report/editor flows:
update,switch,status,diff,replay,replay-range. - Write:
change-rev-prop,change-rev-prop2,lock/unlock(including*-many), and a low-levelcommitAPI driven byEditorCommand.
For full API docs and examples, see https://docs.rs/svn.
Errors
All APIs return svn::Result<T> (an alias for Result<T, SvnError>). Server-side
failures are returned as SvnError::Server(ServerError) and include a structured
error chain and command context.
use svn::{RaSvnClient, SvnError, SvnUrl};
#[tokio::main]
async fn main() {
let url = SvnUrl::parse("svn://example.com/repo").unwrap();
let client = RaSvnClient::new(url, None, None);
let err = client.get_latest_rev().await.unwrap_err();
match err {
SvnError::Server(server) => {
eprintln!("server: {server}");
for item in &server.chain {
eprintln!(" code={} msg={:?} file={:?} line={:?}", item.code, item.message, item.file, item.line);
}
}
other => eprintln!("error: {other}"),
}
}
Logging
This crate uses tracing for debug logging. Enable logs in your application
(for example with tracing-subscriber) and set an appropriate filter:
RUST_LOG=svn=debug
Compatibility
- Protocol:
ra_svnv2 (svn://, andsvn+ssh://with thesshfeature). - IPv6: supported via bracketed URLs (for example
svn://[::1]/repo). - MSRV: Rust
1.92.0(seeCargo.toml). - Optional
serdesupport via theserdefeature. - Optional Cyrus SASL support via
cyrus-sasl(runtimelibsasl2). - Optional SSH tunnel support via
ssh(russh).
Security
svn://is plain TCP (no native TLS).svn+ssh://uses SSH for transport encryption and authentication.PLAINsends credentials without encryption unless you use a secure tunnel (VPN / SSH port forwarding / stunnel) or negotiate a SASL security layer.- Even with
CRAM-MD5, repository traffic is still unencrypted unless a tunnel or SASL security layer is used.
Testing
Unit tests and property tests:
cargo test --all-features
Interop tests against a real svnserve (requires svn, svnadmin, svnserve):
SVN_INTEROP=1 cargo test --all-features --test interop_svnserve -- --nocapture
Limitations
- Not a working copy client (no checkout / update of a local working copy).
- No native TLS (see Security).
- The
sshfeature supports a subset of~/.ssh/configand ssh-agent (no ProxyJump/ProxyCommand).
License
Licensed under the MIT license. See LICENSE.
Dependencies
~9β45MB
~575K SLoC