Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:

# Linux builds
- arch: amd64
run_on: ubuntu-latest
run_on: ubuntu-24.04
os: linux
target: x86_64-unknown-linux-gnu
test: true
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# It will then package that binary into the image, and use that as the entrypoint.
# This means that running `docker build` is not a repeatable way to build the same
# image, but the benefit is much faster cross-platform builds; a net win.
FROM debian:bookworm-slim
FROM ubuntu:24.04

LABEL org.opencontainers.image.source=https://github.com/SierraSoftworks/github-backup
LABEL org.opencontainers.image.description="Backup your GitHub repositories and releases automatically"
Expand Down
1 change: 0 additions & 1 deletion src/engines/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ mod tests {
#[cfg_attr(feature = "pure_tests", ignore)]
#[rstest]
#[case("SierraSoftworks/grey", "https://github.com/sierrasoftworks/grey.git")]
#[case("neovim/neovim", "https://github.com/neovim/neovim.git")]
#[tokio::test]
async fn test_backup(#[case] name: &str, #[case] url: &str) {
let temp_dir = tempfile::tempdir().expect("a temporary directory");
Expand Down
4 changes: 2 additions & 2 deletions src/engines/http_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl BackupEngine<HttpFile> for HttpFileEngine {
tokio::fs::remove_file(&temp_path)
.await
.unwrap_or_else(|e| {
tracing::error!(
error!(
"Failed to remove temporary backup file '{}': {}",
temp_path.display(),
e
Expand All @@ -175,7 +175,7 @@ impl BackupEngine<HttpFile> for HttpFileEngine {
tokio::fs::remove_file(&temp_path)
.await
.unwrap_or_else(|e| {
tracing::error!(
error!(
"Failed to remove temporary backup file '{}': {}",
temp_path.display(),
e
Expand Down
8 changes: 3 additions & 5 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
human_errors::error_shim!(Error);

use std::convert;

use reqwest::StatusCode;

impl convert::From<reqwest::Error> for Error {
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
if err.is_connect() {
user_with_internal(
Expand Down Expand Up @@ -35,7 +33,7 @@ impl convert::From<reqwest::Error> for Error {
}
}

impl convert::From<reqwest::Response> for Error {
impl From<reqwest::Response> for Error {
fn from(resp: reqwest::Response) -> Self {
match resp.status() {
StatusCode::NOT_FOUND => user(
Expand All @@ -55,7 +53,7 @@ impl convert::From<reqwest::Response> for Error {
}
}

impl convert::From<reqwest::header::InvalidHeaderValue> for Error {
impl From<reqwest::header::InvalidHeaderValue> for Error {
fn from(err: reqwest::header::InvalidHeaderValue) -> Self {
system_with_internal(
"Could not parse header value due to an internal error.",
Expand Down
2 changes: 1 addition & 1 deletion src/filter/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Debug for Expr<'_> {
}

struct ExprPrinter<'a, 'b>(&'a mut std::fmt::Formatter<'b>);
impl<'a, 'b> ExprVisitor<std::fmt::Result> for ExprPrinter<'a, 'b> {
impl ExprVisitor<std::fmt::Result> for ExprPrinter<'_, '_> {
fn visit_literal(&mut self, value: &FilterValue) -> std::fmt::Result {
write!(self.0, "{}", value)
}
Expand Down
2 changes: 1 addition & 1 deletion src/filter/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl<'a, T: Filterable> FilterContext<'a, T> {
}
}

impl<'a, T: Filterable> ExprVisitor<FilterValue> for FilterContext<'a, T> {
impl<T: Filterable> ExprVisitor<FilterValue> for FilterContext<'_, T> {
fn visit_literal(&mut self, value: &FilterValue) -> FilterValue {
value.clone()
}
Expand Down
72 changes: 36 additions & 36 deletions src/filter/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,81 +159,81 @@ impl<'a> Iterator for Scanner<'a> {
))));
}
'&' => {
if self.match_char('&') {
return Some(Ok(Token::And(Loc::new(
return if self.match_char('&') {
Some(Ok(Token::And(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Err(errors::user(
&format!("Filter included an orphaned '&' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '&&' operator to implement a logical AND within your filter."
)));
Some(Err(errors::user(
&format!("Filter included an orphaned '&' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '&&' operator to implement a logical AND within your filter."
)))
}
}
'|' => {
if self.match_char('|') {
return Some(Ok(Token::Or(Loc::new(
return if self.match_char('|') {
Some(Ok(Token::Or(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Err(errors::user(
&format!("Filter included an orphaned '|' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '||' operator to implement a logical OR within your filter."
)));
Some(Err(errors::user(
&format!("Filter included an orphaned '|' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '||' operator to implement a logical OR within your filter."
)))
}
}
'=' => {
if self.match_char('=') {
return Some(Ok(Token::Equals(Loc::new(
return if self.match_char('=') {
Some(Ok(Token::Equals(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Err(errors::user(
&format!("Filter included an orphaned '=' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '==' operator to implement a logical equality within your filter."
)));
Some(Err(errors::user(
&format!("Filter included an orphaned '=' at {} which is not a valid operator.", Loc::new(self.line, 1 + idx - self.line_start)),
"Ensure that you are using the '==' operator to implement a logical equality within your filter."
)))
}
}
'!' => {
if self.match_char('=') {
return Some(Ok(Token::NotEquals(Loc::new(
return if self.match_char('=') {
Some(Ok(Token::NotEquals(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Ok(Token::Not(Loc::new(
Some(Ok(Token::Not(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
}
}
'>' => {
if self.match_char('=') {
return Some(Ok(Token::GreaterEqual(Loc::new(
return if self.match_char('=') {
Some(Ok(Token::GreaterEqual(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Ok(Token::GreaterThan(Loc::new(
Some(Ok(Token::GreaterThan(Loc::new(
self.line,
idx - self.line_start,
))));
))))
}
}
'<' => {
if self.match_char('=') {
return Some(Ok(Token::SmallerEqual(Loc::new(
return if self.match_char('=') {
Some(Ok(Token::SmallerEqual(Loc::new(
self.line,
1 + idx - self.line_start,
))));
))))
} else {
return Some(Ok(Token::SmallerThan(Loc::new(
Some(Ok(Token::SmallerThan(Loc::new(
self.line,
idx - self.line_start,
))));
))))
}
}
'"' => {
Expand Down
18 changes: 9 additions & 9 deletions src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ impl Filter {
let filter_ptr = NonNull::from(&filter);
let pinned = Box::into_pin(filter);

let tokens = crate::filter::lexer::Scanner::new(unsafe { filter_ptr.as_ref() });
let ast = crate::filter::parser::Parser::parse(tokens.into_iter())?;
let tokens = lexer::Scanner::new(unsafe { filter_ptr.as_ref() });
let ast = parser::Parser::parse(tokens.into_iter())?;
Ok(Self {
filter: pinned,
ast,
Expand Down Expand Up @@ -71,6 +71,13 @@ impl<'de> serde::Deserialize<'de> for Filter {
formatter.write_str("a valid filter expression")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Filter::new(v).map_err(serde::de::Error::custom)
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
Expand All @@ -84,13 +91,6 @@ impl<'de> serde::Deserialize<'de> for Filter {
{
deserializer.deserialize_str(self)
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Filter::new(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_option(FilterVisitor)
Expand Down
14 changes: 7 additions & 7 deletions src/filter/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {
self.tokens.peek(),
Some(Ok(Token::Equals(..)) | Ok(Token::NotEquals(..)))
) {
let token = self.tokens.next().unwrap().unwrap();
let token = self.tokens.next().unwrap()?;
let right = self.comparison()?;
expr = Expr::Binary(Box::new(expr), token, Box::new(right));
}
Expand All @@ -89,7 +89,7 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {
| Some(Ok(Token::SmallerThan(..)))
| Some(Ok(Token::SmallerEqual(..)))
) {
let token = self.tokens.next().unwrap().unwrap();
let token = self.tokens.next().unwrap()?;
let right = self.unary()?;
expr = Expr::Binary(Box::new(expr), token, Box::new(right));
}
Expand All @@ -99,7 +99,7 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {

fn unary(&mut self) -> Result<Expr<'a>, Error> {
if matches!(self.tokens.peek(), Some(Ok(Token::Not(..)))) {
let token = self.tokens.next().unwrap().unwrap();
let token = self.tokens.next().unwrap()?;
let right = self.unary()?;
Ok(Expr::Unary(token, Box::new(right)))
} else {
Expand All @@ -110,7 +110,7 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {
fn primary(&mut self) -> Result<Expr<'a>, Error> {
match self.tokens.peek() {
Some(Ok(Token::LeftParen(..))) => {
let start = self.tokens.next().unwrap().unwrap();
let start = self.tokens.next().unwrap()?;
let expr = self.or()?;
if let Some(Ok(Token::RightParen(..))) = self.tokens.next() {
Ok(expr)
Expand All @@ -122,7 +122,7 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {
}
}
Some(Ok(Token::LeftBracket(..))) => {
let start = self.tokens.next().unwrap().unwrap();
let start = self.tokens.next().unwrap()?;
let mut items = Vec::new();
while !matches!(self.tokens.peek(), Some(Ok(Token::RightBracket(..)))) {
items.push(self.literal()?);
Expand Down Expand Up @@ -162,13 +162,13 @@ impl<'a, I: Iterator<Item = Result<Token<'a>, Error>>> Parser<'a, I> {
match self.tokens.next() {
Some(Ok(Token::True(..))) => Ok(true.into()),
Some(Ok(Token::False(..))) => Ok(false.into()),
Some(Ok(Token::Number(loc, n))) => Ok(super::FilterValue::Number(n.parse().map_err(|e| errors::user_with_internal(
Some(Ok(Token::Number(loc, n))) => Ok(FilterValue::Number(n.parse().map_err(|e| errors::user_with_internal(
&format!("Failed to parse the number '{n}' which you provided at {}.", loc),
"Please make sure that the number is well formatted. It should be in the form 123, or 123.45.",
e,
))?)),
Some(Ok(Token::String(.., s))) => Ok(s.replace("\\\"", "\"").replace("\\\\", "\\").into()),
Some(Ok(Token::Null(..))) => Ok(super::FilterValue::Null),
Some(Ok(Token::Null(..))) => Ok(FilterValue::Null),
Some(Ok(token)) => Err(errors::user(
&format!("While parsing your filter, we found an unexpected '{}' at {}.", token, token.location()),
"Make sure that you have written a valid filter query.",
Expand Down
42 changes: 21 additions & 21 deletions src/filter/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,27 @@ impl PartialEq for FilterValue {
}

impl PartialOrd for FilterValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(FilterValue::Null, FilterValue::Null) => Some(Ordering::Equal),
(FilterValue::Bool(a), FilterValue::Bool(b)) => a.partial_cmp(b),
(FilterValue::Number(a), FilterValue::Number(b)) => a.partial_cmp(b),
(FilterValue::String(a), FilterValue::String(b)) => a.partial_cmp(b),
(FilterValue::Tuple(a), FilterValue::Tuple(b)) => {
if a.len() != b.len() {
a.len().partial_cmp(&b.len())
} else {
a.iter()
.zip(b.iter())
.map(|(x, y)| x.partial_cmp(y))
.find(|&cmp| cmp != Some(Ordering::Equal))
.unwrap_or(Some(Ordering::Equal))
}
}
_ => None, // Return None for non-comparable types
}
}

fn lt(&self, other: &Self) -> bool {
match (self, other) {
(FilterValue::Null, FilterValue::Null) => true,
Expand Down Expand Up @@ -145,27 +166,6 @@ impl PartialOrd for FilterValue {
_ => false,
}
}

fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(FilterValue::Null, FilterValue::Null) => Some(Ordering::Equal),
(FilterValue::Bool(a), FilterValue::Bool(b)) => a.partial_cmp(b),
(FilterValue::Number(a), FilterValue::Number(b)) => a.partial_cmp(b),
(FilterValue::String(a), FilterValue::String(b)) => a.partial_cmp(b),
(FilterValue::Tuple(a), FilterValue::Tuple(b)) => {
if a.len() != b.len() {
a.len().partial_cmp(&b.len())
} else {
a.iter()
.zip(b.iter())
.map(|(x, y)| x.partial_cmp(y))
.find(|&cmp| cmp != Some(Ordering::Equal))
.unwrap_or(Some(Ordering::Equal))
}
}
_ => None, // Return None for non-comparable types
}
}
}

impl Display for FilterValue {
Expand Down
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ async fn run(args: Args) -> Result<(), Error> {
.and_then(|s| s.find_next_occurrence(&chrono::Utc::now(), false).ok());

{
let _span = tracing::info_span!("backup.all").entered();
let _span = info_span!("backup.all").entered();

for policy in config.backups.iter() {
let _policy_span = tracing::info_span!("backup.policy", policy = %policy).entered();
let _policy_span = info_span!("backup.policy", policy = %policy).entered();

match policy.kind.as_str() {
k if k == GitHubArtifactKind::Repo.as_str() => {
Expand Down Expand Up @@ -130,7 +130,7 @@ impl<E: BackupEntity> PairingHandler<E> for LoggingPairingHandler {
info!(" - {} ({})", entity, state);
}

fn on_error(&self, error: crate::Error) {
fn on_error(&self, error: Error) {
warn!("Error: {}", error);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/telemetry/traced_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct TracedStream<T: Stream> {
}

pub trait StreamExt: Stream + Sized {
fn trace(self, span: tracing::Span) -> TracedStream<Self> {
fn trace(self, span: Span) -> TracedStream<Self> {
TracedStream { stream: self, span }
}
}
Expand Down