Skip to content

Commit 187c8d6

Browse files
committed
add webdav command
1 parent 7f00f6e commit 187c8d6

File tree

4 files changed

+421
-1
lines changed

4 files changed

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

0 commit comments

Comments
 (0)