Skip to content

Commit 33c3feb

Browse files
committed
add webdav command
1 parent 7f00f6e commit 33c3feb

File tree

4 files changed

+423
-1
lines changed

4 files changed

+423
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ mimalloc = { version = "0.1.39", default_features = false, optional = true }
8989
rhai = { workspace = true }
9090
simplelog = { workspace = true }
9191
runtime-format = "0.1.3"
92+
webdav-handler = {version = "0.2.0", features = ["warp-compat"]}
93+
bytes = "1.5.0"
94+
futures = "0.3.30"
95+
tokio = "1.35.1"
96+
warp = "0.3.6"
9297

9398
[dev-dependencies]
9499
aho-corasick = { workspace = true }

src/commands.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub(crate) mod self_update;
2323
pub(crate) mod show_config;
2424
pub(crate) mod snapshots;
2525
pub(crate) mod tag;
26+
pub(crate) mod webdav;
2627

2728
use std::fs::File;
2829
use std::path::PathBuf;
@@ -36,7 +37,7 @@ use crate::{
3637
init::InitCmd, key::KeyCmd, list::ListCmd, ls::LsCmd, merge::MergeCmd, mount::MountCmd,
3738
prune::PruneCmd, repair::RepairCmd, repoinfo::RepoInfoCmd, restore::RestoreCmd,
3839
self_update::SelfUpdateCmd, show_config::ShowConfigCmd, snapshots::SnapshotCmd,
39-
tag::TagCmd,
40+
tag::TagCmd, webdav::WebDavCmd,
4041
},
4142
config::{progress_options::ProgressOptions, RusticConfig},
4243
{Application, RUSTIC_APP},
@@ -129,6 +130,9 @@ enum RusticCmd {
129130

130131
/// Change tags of snapshots
131132
Tag(TagCmd),
133+
134+
/// Mount repository
135+
Webdav(WebDavCmd),
132136
}
133137

134138
/// Entry point for the application. It needs to be a struct to allow using subcommands!

src/commands/webdav.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//! `mount` subcommand
2+
mod webdavfs;
3+
4+
use std::net::SocketAddr;
5+
use std::str::FromStr;
6+
7+
use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
8+
use abscissa_core::{Command, Runnable, Shutdown};
9+
use anyhow::Result;
10+
use webdav_handler::{warp::dav_handler, DavHandler};
11+
use webdavfs::RusticWebDavFS;
12+
13+
#[derive(clap::Parser, Command, Debug)]
14+
pub(crate) struct WebDavCmd {
15+
/// The path template to use for snapshots. {id}, {id_long}, {time}, {username}, {hostname}, {label}, {tags}, {backup_start}, {backup_end} are replaced. [default: "[{hostname}]/[{label}]/{time}"]
16+
#[clap(long)]
17+
path_template: Option<String>,
18+
19+
/// The time template to use to display times in the path template. See https://docs.rs/chrono/latest/chrono/format/strftime/index.html for format options. [default: "%Y-%m-%d_%H-%M-%S"]
20+
#[clap(long)]
21+
time_template: Option<String>,
22+
23+
/// Use symlinks. This may not be supported by all WebDAV clients
24+
#[clap(long)]
25+
symlinks: bool,
26+
27+
/// Socket address to use
28+
#[clap(value_name = "ADDR")]
29+
socket: String,
30+
31+
/// Specify directly which path to mount
32+
#[clap(value_name = "SNAPSHOT[:PATH]")]
33+
snap: Option<String>,
34+
}
35+
36+
impl Runnable for WebDavCmd {
37+
fn run(&self) {
38+
if let Err(err) = self.inner_run() {
39+
status_err!("{}", err);
40+
RUSTIC_APP.shutdown(Shutdown::Crash);
41+
};
42+
}
43+
}
44+
45+
impl WebDavCmd {
46+
fn inner_run(&self) -> Result<()> {
47+
let config = RUSTIC_APP.config();
48+
49+
let repo = open_repository(&config)?.to_indexed()?;
50+
51+
let path_template = self
52+
.path_template
53+
.clone()
54+
.unwrap_or("[{hostname}]/[{label}]/{time}".to_string());
55+
let time_template = self
56+
.time_template
57+
.clone()
58+
.unwrap_or("%Y-%m-%d_%H-%M-%S".to_string());
59+
60+
let sn_filter = |sn: &_| config.snapshot_filter.matches(sn);
61+
let target_fs = if let Some(snap) = &self.snap {
62+
let node = repo.node_from_snapshot_path(snap, sn_filter)?;
63+
RusticWebDavFS::from_node(repo, node)
64+
} else {
65+
let snapshots = repo.get_matching_snapshots(sn_filter)?;
66+
RusticWebDavFS::from_snapshots(
67+
repo,
68+
snapshots,
69+
path_template,
70+
time_template,
71+
self.symlinks,
72+
)?
73+
};
74+
let addr = SocketAddr::from_str(&self.socket)?;
75+
let dav_server = DavHandler::builder().filesystem(target_fs).build_handler();
76+
77+
tokio::runtime::Builder::new_current_thread()
78+
.enable_all()
79+
.build()?
80+
.block_on(async {
81+
warp::serve(dav_handler(dav_server)).run(addr).await;
82+
});
83+
84+
Ok(())
85+
}
86+
}

0 commit comments

Comments
 (0)