Skip to content

Commit

Permalink
Merge branch 'main' into rclone-semver
Browse files Browse the repository at this point in the history
  • Loading branch information
simonsan authored Mar 11, 2024
2 parents 7fcfeae + ae840b5 commit 933417f
Show file tree
Hide file tree
Showing 50 changed files with 546 additions and 203 deletions.
61 changes: 60 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ members = [
resolver = "2"

[workspace.package]
rust-version = "1.72.0"
rust-version = "1.72.1"

[workspace.dependencies]
aho-corasick = "1.1.2"
Expand Down Expand Up @@ -61,3 +61,62 @@ rpath = false
lto = true
debug-assertions = false
codegen-units = 1

[workspace.lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
rust_2018_idioms = "warn"
trivial_casts = "warn"
unused_lifetimes = "warn"
unused_qualifications = "warn"
bad_style = "warn"
dead_code = "allow" # TODO: "warn"
improper_ctypes = "warn"
missing_copy_implementations = "warn"
missing_debug_implementations = "warn"
non_shorthand_field_patterns = "warn"
no_mangle_generic_items = "warn"
overflowing_literals = "warn"
path_statements = "warn"
patterns_in_fns_without_body = "warn"
trivial_numeric_casts = "warn"
unused_results = "warn"
unused_extern_crates = "warn"
unused_import_braces = "warn"
unconditional_recursion = "warn"
unused = "warn"
unused_allocation = "warn"
unused_comparisons = "warn"
unused_parens = "warn"
while_true = "warn"
unreachable_pub = "allow"

[workspace.lints.clippy]
redundant_pub_crate = "allow"
pedantic = "warn"
nursery = "warn"
# expect_used = "warn" # TODO!
# unwrap_used = "warn" # TODO!
enum_glob_use = "warn"
correctness = "warn"
suspicious = "warn"
complexity = "warn"
perf = "warn"
cast_lossless = "warn"
default_trait_access = "warn"
doc_markdown = "warn"
manual_string_new = "warn"
match_same_arms = "warn"
semicolon_if_nothing_returned = "warn"
trivially_copy_pass_by_ref = "warn"
module_name_repetitions = "allow"
# TODO: Remove when Windows support landed
# mostly Windows-related functionality is missing `const`
# as it's only OK(()), but doesn't make it reasonable to
# have a breaking change in the future. They won't be const.
missing_const_for_fn = "allow"
needless_raw_string_hashes = "allow"

[workspace.lints.rustdoc]
# We run rustdoc with `--document-private-items` so we can document private items
private_intra_doc_links = "allow"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Please make sure, that you read the

## Minimum Rust version policy

This crate's minimum supported `rustc` version is `1.72.0`.
This crate's minimum supported `rustc` version is `1.72.1`.

The current policy is that the minimum Rust version required to use this crate
can be increased in minor version updates. For example, if `crate 1.0` requires
Expand Down
3 changes: 3 additions & 0 deletions crates/backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,6 @@ opendal = { version = "0.45", features = ["services-b2", "services-swift", "laye

[dev-dependencies]
rstest = { workspace = true }

[lints]
workspace = true
31 changes: 28 additions & 3 deletions crates/backend/src/choose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,48 @@ pub struct BackendOptions {
/// * `right` - The right value
#[cfg(feature = "merge")]
pub fn extend<A, T: Extend<A> + IntoIterator<Item = A>>(left: &mut T, right: T) {
left.extend(right)
left.extend(right);
}

impl BackendOptions {
/// Convert the options to backends.
///
/// # Errors
///
/// If the repository is not given, an error is returned.
///
/// # Returns
///
/// The backends for the repository.
pub fn to_backends(&self) -> Result<RepositoryBackends> {
let mut options = self.options.clone();
options.extend(self.options_cold.clone());
let be = self
.get_backend(self.repository.as_ref(), options)?
.ok_or(anyhow!("No repository given."))?;
.ok_or_else(|| anyhow!("No repository given."))?;
let mut options = self.options.clone();
options.extend(self.options_hot.clone());
let be_hot = self.get_backend(self.repo_hot.as_ref(), options)?;

Ok(RepositoryBackends::new(be, be_hot))
}

/// Get the backend for the given repository.
///
/// # Arguments
///
/// * `repo_string` - The repository string to use.
/// * `options` - Additional options for the backend.
///
/// # Errors
///
/// If the backend cannot be loaded, an error is returned.
///
/// # Returns
///
/// The backend for the given repository.
// Allow unused_self, as we want to access this method
#[allow(clippy::unused_self)]
fn get_backend(
&self,
repo_string: Option<&String>,
Expand Down Expand Up @@ -137,7 +162,7 @@ pub trait BackendChoice {
///
/// If the url is a windows path, the type will be "local".
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, EnumString, Display)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
pub enum SupportedBackend {
/// A local backend
#[strum(serialize = "local", to_string = "Local Backend")]
Expand Down
4 changes: 4 additions & 0 deletions crates/backend/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ pub enum LocalBackendErrorKind {
CommandExecutionFailed(std::io::Error),
/// command was not successful for filename {file_name}, type {file_type}, id {id}: {status}
CommandNotSuccessful {
/// File name
file_name: String,
/// File type
file_type: String,
/// Item ID
id: String,
/// Exit status
status: ExitStatus,
},
/// error building automaton `{0:?}`
Expand Down
8 changes: 7 additions & 1 deletion crates/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The primary types in this crate are:
The following backends are currently supported and can be enabled with features:
- `LocalBackend` - Backend for accessing a local filesystem.
- `OpenDALBackend` - Backend for accessing a OpenDAL filesystem.
- `OpenDALBackend` - Backend for accessing a `OpenDAL` filesystem.
- `RcloneBackend` - Backend for accessing a Rclone filesystem.
- `RestBackend` - Backend for accessing a REST API.
- `SftpBackend` - Backend for accessing a SFTP filesystem.
Expand Down Expand Up @@ -60,14 +60,20 @@ This crate exposes a few features for controlling dependency usage:
*/

pub mod choose;
/// Error types for the backend.
pub mod error;
/// Local backend for Rustic.
pub mod local;
/// `OpenDAL` backend for Rustic.
#[cfg(feature = "opendal")]
pub mod opendal;
/// `Rclone` backend for Rustic.
#[cfg(feature = "rclone")]
pub mod rclone;
/// REST backend for Rustic.
#[cfg(feature = "rest")]
pub mod rest;
/// Utility functions for the backend.
pub mod util;

// rustic_backend Public API
Expand Down
1 change: 1 addition & 0 deletions crates/backend/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustic_core::{FileType, Id, ReadBackend, WriteBackend, ALL_FILE_TYPES};

use crate::error::LocalBackendErrorKind;

/// A local backend.
#[derive(Clone, Debug)]
pub struct LocalBackend {
/// The base path of the backend.
Expand Down
27 changes: 25 additions & 2 deletions crates/backend/src/opendal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/// `OpenDAL` S3 backend for Rustic.
#[cfg(feature = "s3")]
pub mod s3;
/// `OpenDAL` SFTP backend for Rustic.
#[cfg(all(unix, feature = "sftp"))]
pub mod sftp;

Expand All @@ -22,6 +24,7 @@ mod consts {
pub(super) const DEFAULT_RETRY: usize = 5;
}

/// `OpenDALBackend` contains a wrapper around an blocking operator of the `OpenDAL` library.
#[derive(Clone, Debug)]
pub struct OpenDALBackend {
operator: BlockingOperator,
Expand All @@ -42,8 +45,16 @@ impl OpenDALBackend {
///
/// # Arguments
///
/// * `path` - The path to the OpenDAL backend.
/// * `options` - Additional options for the OpenDAL backend.
/// * `path` - The path to the `OpenDAL` backend.
/// * `options` - Additional options for the `OpenDAL` backend.
///
/// # Errors
///
/// If the path is not a valid `OpenDAL` path, an error is returned.
///
/// # Returns
///
/// A new `OpenDAL` backend.
pub fn new(path: impl AsRef<str>, options: HashMap<String, String>) -> Result<Self> {
let max_retries = match options.get("retry").map(String::as_str) {
Some("false" | "off") => 0,
Expand Down Expand Up @@ -72,6 +83,18 @@ impl OpenDALBackend {
Ok(Self { operator })
}

/// Return a path for the given file type and id.
///
/// # Arguments
///
/// * `tpe` - The type of the file.
/// * `id` - The id of the file.
///
/// # Returns
///
/// The path for the given file type and id.
// Let's keep this for now, as it's being used in the trait implementations.
#[allow(clippy::unused_self)]
fn path(&self, tpe: FileType, id: &Id) -> String {
let hex_id = id.to_hex();
match tpe {
Expand Down
21 changes: 21 additions & 0 deletions crates/backend/src/opendal/s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::opendal::OpenDALBackend;
use bytes::Bytes;
use rustic_core::{FileType, Id, ReadBackend, WriteBackend};

/// S3 backend for Rustic. A wrapper around the `OpenDAL` backend.
#[derive(Clone, Debug)]
pub struct S3Backend(OpenDALBackend);

Expand Down Expand Up @@ -62,6 +63,18 @@ impl S3Backend {
/// * `path` - The path to the s3 bucket
/// * `options` - Additional options for the s3 backend
///
/// # Errors
///
/// If the path is not a valid url, an error is returned.
///
/// # Returns
///
/// The new S3 backend.
///
/// # Panics
///
/// If the scheme cannot be set to `https`. This is a security measurement for now.
///
/// # Notes
///
/// The path should be something like "`https://s3.amazonaws.com/bucket/my/repopath`"
Expand All @@ -79,6 +92,8 @@ impl S3Backend {
if url.has_host() {
if url.scheme().is_empty() {
url.set_scheme("https")
// TODO: This is a security measurement for now. We should not allow http.
// This error should be handled gracefully though somewhere else.
.expect("could not set scheme to https");
}
url.set_path("");
Expand All @@ -93,6 +108,12 @@ impl S3Backend {
Ok(Self(OpenDALBackend::new("s3", options)?))
}

/// Convert the S3 backend to the inner `OpenDAL` backend.
///
/// # Returns
///
/// The inner `OpenDAL` backend.
#[must_use]
pub fn to_inner(self) -> OpenDALBackend {
self.0
}
Expand Down
11 changes: 11 additions & 0 deletions crates/backend/src/opendal/sftp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::opendal::OpenDALBackend;
use bytes::Bytes;
use rustic_core::{FileType, Id, ReadBackend, WriteBackend};

/// SFTP backend for Rustic. A wrapper around the `OpenDAL` backend.
#[derive(Clone, Debug)]
pub struct SftpBackend(OpenDALBackend);

Expand Down Expand Up @@ -61,6 +62,14 @@ impl SftpBackend {
/// * `path` - The path to the sftp server
/// * `options` - Additional options for the SFTP backend
///
/// # Errors
///
/// If the path is not a valid url, an error is returned.
///
/// # Returns
///
/// The new SFTP backend.
///
/// # Notes
///
/// The path should be something like "`sftp://user@host:port/path`"
Expand All @@ -85,6 +94,8 @@ impl SftpBackend {
Ok(Self(OpenDALBackend::new("sftp", options)?))
}

/// Return the inner `OpenDAL` backend.
#[must_use]
pub fn to_inner(self) -> OpenDALBackend {
self.0
}
Expand Down
24 changes: 17 additions & 7 deletions crates/backend/src/rclone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ impl Drop for RcloneBackend {
fn drop(&mut self) {
debug!("killing rclone.");
self.child.kill().unwrap();
self.handle.take().map(JoinHandle::join);
// TODO: Handle error and log it
_ = self.handle.take().map(JoinHandle::join);
}
}

Expand Down Expand Up @@ -116,6 +117,15 @@ impl RcloneBackend {
/// [`RcloneErrorKind::NoStdOutForRclone`]: RcloneErrorKind::NoStdOutForRclone
/// [`RcloneErrorKind::RCloneExitWithBadStatus`]: RcloneErrorKind::RCloneExitWithBadStatus
/// [`RcloneErrorKind::UrlNotStartingWithHttp`]: RcloneErrorKind::UrlNotStartingWithHttp
///
/// # Returns
///
/// The created [`RcloneBackend`].
///
/// # Panics
///
/// If the rclone command is not found.
// TODO: This should be an error, not a panic.
pub fn new(url: impl AsRef<str>, options: HashMap<String, String>) -> Result<Self> {
let rclone_command = options.get("rclone-command");
let use_password = options
Expand All @@ -140,20 +150,20 @@ impl RcloneBackend {
let user = Alphanumeric.sample_string(&mut thread_rng(), 12);
let password = Alphanumeric.sample_string(&mut thread_rng(), 12);

let mut rclone_command = split(
rclone_command
.map(String::as_str)
.unwrap_or("rclone serve restic --addr localhost:0"),
)?;
let mut rclone_command =
split(rclone_command.map_or("rclone serve restic --addr localhost:0", String::as_str))?;
rclone_command.push(url.as_ref().to_string());
debug!("starting rclone via {rclone_command:?}");

let mut command = Command::new(&rclone_command[0]);

if use_password {
command
// TODO: We should handle errors here
_ = command
.env("RCLONE_USER", &user)
.env("RCLONE_PASS", &password);
}

let mut child = command
.args(&rclone_command[1..])
.stderr(Stdio::piped())
Expand Down
Loading

0 comments on commit 933417f

Please sign in to comment.