diff --git a/src/command_runner.rs b/src/command_runner.rs index 0b73422..268bff7 100644 --- a/src/command_runner.rs +++ b/src/command_runner.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::error::Error; use std::ffi::OsStr; use std::io::{Result as IoResult, Write}; @@ -5,53 +6,40 @@ use std::process::Command; use std::process::Output; use std::process::Stdio; +#[macro_export] +macro_rules! args { + ($($x:expr),*) => ( + &[$($x.as_ref()),*] + ); + ($($x:expr,)*) => (args![$($x),*]) // handle trailing commas +} + pub trait CommandRunner { - fn run_with_args_and_stdin + ?Sized>( + fn run_with_args_and_stdin( &self, program: &str, - args: &[&S], + args: &[&OsStr], stdin: &str, ) -> IoResult; - fn run_with_args + ?Sized>( - &self, - program: &str, - args: &[&S], - ) -> IoResult { + fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult { self.run_with_args_and_stdin(program, args, "") } - fn get_output + ?Sized>( - &self, - program: &str, - args: &[&S], - ) -> Result, Box> { + fn get_output(&self, program: &str, args: &[&OsStr]) -> Result, Box> { let output = self.run_with_args(program, args)?; if !output.status.success() { return Err(String::from_utf8(output.stderr)?.into()); } Ok(output.stdout) } - fn get_stderr + ?Sized>( - &self, - program: &str, - args: &[&S], - ) -> Result, Box> { + fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result, Box> { let output = self.run_with_args(program, args)?; if !output.status.success() { return Err(String::from_utf8(output.stderr)?.into()); } Ok(output.stderr) } - fn run_successfully + ?Sized>( - &self, - program: &str, - args: &[&S], - ) -> Result<(), Box> { - let output = self.run_with_args(program, args)?; - if output.status.success() { - Ok(()) - } else { - Err(String::from_utf8(output.stderr)?.into()) - } + fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box> { + self.get_output(program, args).map(|_| ()) } } @@ -59,10 +47,10 @@ pub trait CommandRunner { pub struct StdCommandRunner; impl CommandRunner for StdCommandRunner { - fn run_with_args_and_stdin + ?Sized>( + fn run_with_args_and_stdin( &self, program: &str, - args: &[&S], + args: &[&OsStr], input: &str, ) -> IoResult { // FIXME: logger @@ -90,14 +78,14 @@ where C: 'a + CommandRunner, { command_runner: &'a C, - user_name: &'a str, + user_name: Cow<'a, str>, } impl<'a, C> SetuidCommandRunner<'a, C> where C: 'a + CommandRunner, { - pub fn new(user_name: &'a str, command_runner: &'a C) -> SetuidCommandRunner<'a, C> { + pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> SetuidCommandRunner<'a, C> { SetuidCommandRunner { command_runner, user_name, @@ -138,13 +126,13 @@ impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C> where C: 'a + CommandRunner, { - fn run_with_args_and_stdin + ?Sized>( + fn run_with_args_and_stdin( &self, program: &str, - args: &[&S], + args: &[&OsStr], input: &str, ) -> IoResult { - let uid = get_user_by_name(self.user_name) + let uid = get_user_by_name(&self.user_name) .expect("User does not exist") .uid(); let set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name)); @@ -196,10 +184,10 @@ impl<'a, C> CommandRunner for SuCommandRunner<'a, C> where C: 'a + CommandRunner, { - fn run_with_args_and_stdin + ?Sized>( + fn run_with_args_and_stdin( &self, program: &str, - args: &[&S], + args: &[&OsStr], input: &str, ) -> IoResult { let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program]; diff --git a/src/factory.rs b/src/factory.rs index 5b3eea6..1aedea2 100644 --- a/src/factory.rs +++ b/src/factory.rs @@ -82,7 +82,7 @@ impl<'a, C: CommandRunner> SymbolRepository<'a> Box::new(Dir::new(value.to_string())), Box::new(Owner::new( value.to_string(), - matches[1].to_string().into(), + matches[1].to_string(), self.command_runner, )), ])) as Box @@ -110,7 +110,7 @@ impl<'a, C: CommandRunner> SymbolRepository<'a> )) as Box) } else if let Some(matches) = self.systemd_linger.captures(value) { Some(Box::new(SystemdUserSession::new( - matches[1].to_string().into(), + matches[1].to_string(), self.command_runner, )) as Box) } else { diff --git a/src/for_each_tuple.rs b/src/for_each_tuple.rs index 25c5f1e..324dc2b 100644 --- a/src/for_each_tuple.rs +++ b/src/for_each_tuple.rs @@ -9,7 +9,7 @@ macro_rules! for_each_tuple_ { } #[macro_export] macro_rules! for_each_tuple { - ( $m:ident ) => ( - for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, } - ); + ( $m:ident ) => { + for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, } + }; } diff --git a/src/lib.rs b/src/lib.rs index 9223116..e81dd87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ mod for_each_tuple; pub mod bin; pub mod build; +#[macro_use] pub mod command_runner; pub mod factory; pub mod loggers; diff --git a/src/symbols/acme/account_key.rs b/src/symbols/acme/account_key.rs index 2b0226d..3ab2099 100644 --- a/src/symbols/acme/account_key.rs +++ b/src/symbols/acme/account_key.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::path::Path; @@ -7,13 +6,13 @@ use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct AcmeAccountKey<'a, C: 'a + CommandRunner> { - path: Cow<'a, Path>, +pub struct AcmeAccountKey<'a, P: AsRef, C: CommandRunner> { + path: P, command_runner: &'a C, } -impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> { - pub fn new(path: Cow<'a, Path>, command_runner: &'a C) -> Self { +impl<'a, P: AsRef, C: CommandRunner> AcmeAccountKey<'a, P, C> { + pub fn new(path: P, command_runner: &'a C) -> Self { AcmeAccountKey { path, command_runner, @@ -25,26 +24,26 @@ impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> { } } -impl<'a, C: CommandRunner> fmt::Display for AcmeAccountKey<'a, C> { +impl<'a, P: AsRef, C: CommandRunner> fmt::Display for AcmeAccountKey<'a, P, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AcmeAccountKey {}", self.path.display()) + write!(f, "AcmeAccountKey {}", self.path.as_ref().display()) } } -impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> { +impl<'a, P: AsRef, C: CommandRunner> Symbol for AcmeAccountKey<'a, P, C> { fn target_reached(&self) -> Result> { - if !self.path.exists() { + if !self.path.as_ref().exists() { return Ok(false); } let stdout = self.command_runner.get_output( "openssl", - &[ - "rsa".as_ref(), - "-in".as_ref(), - self.path.as_os_str(), - "-noout".as_ref(), - "-check".as_ref(), - "-text".as_ref(), + args![ + "rsa", + "-in", + self.path.as_ref(), + "-noout", + "-check", + "-text", ], )?; Ok(stdout.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes())) @@ -53,20 +52,21 @@ impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> { fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "openssl", - &[ - "genrsa".as_ref(), - "-out".as_ref(), - self.path.as_os_str(), - self.get_bytes().to_string().as_ref(), + args![ + "genrsa", + "-out", + self.path.as_ref(), + self.get_bytes().to_string(), ], ) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new( - "dir", - self.path.parent().unwrap().to_string_lossy(), - )] + if let Some(parent) = self.path.as_ref().parent() { + vec![Resource::new("dir", parent.to_str().unwrap())] + } else { + vec![] + } } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/acme/cert.rs b/src/symbols/acme/cert.rs index 2796b9b..e3f46cb 100644 --- a/src/symbols/acme/cert.rs +++ b/src/symbols/acme/cert.rs @@ -1,67 +1,66 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::fs::File as FsFile; use std::io::Write; -use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct AcmeCert<'a, C: 'a + CommandRunner> { - domain: Cow<'a, str>, +pub struct AcmeCert<'a, D: AsRef, C: CommandRunner> { + domain: D, command_runner: &'a C, } -impl<'a, C: CommandRunner> AcmeCert<'a, C> { - pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, D: AsRef, C: CommandRunner> AcmeCert<'a, D, C> { + pub fn new(domain: D, command_runner: &'a C) -> Self { AcmeCert { domain, command_runner, } } - fn get_csr_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.csr", self.domain) + fn get_csr_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.csr", self.domain.as_ref()).into() } - fn get_cert_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.crt", self.domain) + fn get_cert_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into() } } -impl<'a, C: CommandRunner> fmt::Display for AcmeCert<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> fmt::Display for AcmeCert<'a, D, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AcmeCert {}", self.domain) + write!(f, "AcmeCert {}", self.domain.as_ref()) } } const DAYS_IN_SECONDS: u32 = 24 * 60 * 60; -impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> Symbol for AcmeCert<'a, D, C> { fn target_reached(&self) -> Result> { - if !Path::new(&self.get_cert_path()).exists() { + if !self.get_cert_path().exists() { return Ok(false); } let output = self.command_runner.run_with_args( "openssl", - &[ + args![ "x509", "-in", - &self.get_cert_path(), + self.get_cert_path(), "-noout", "-subject", "-checkend", - &(30 * DAYS_IN_SECONDS).to_string(), + (30 * DAYS_IN_SECONDS).to_string(), ], )?; if output.status.success() && output.stdout == format!( "subject=CN = {}\nCertificate will not expire\n", - self.domain + self.domain.as_ref() ) .as_bytes() { @@ -70,18 +69,22 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> { .command_runner .run_successfully( "openssl", - &[ + args![ "verify", "--untrusted", "/home/acme/lets_encrypt_x3_cross_signed.pem", - &self.get_cert_path(), + self.get_cert_path(), ], ) .is_ok(), ) } else if output.status.code() == Some(1) && output.stdout - == format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes() + == format!( + "subject=CN = {}\nCertificate will expire\n", + self.domain.as_ref() + ) + .as_bytes() { Ok(false) } else { @@ -92,11 +95,11 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> { fn execute(&self) -> Result<(), Box> { let output = self.command_runner.get_output( "acme-tiny", - &[ + args![ "--account-key", "/home/acme/account.key", "--csr", - &self.get_csr_path(), + self.get_csr_path(), "--acme-dir", "/home/acme/challenges/", ], @@ -107,7 +110,7 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> { } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("file", self.get_csr_path())] + vec![Resource::new("file", self.get_csr_path().to_str().unwrap())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/acme/chain.rs b/src/symbols/acme/chain.rs index c8c0ddc..5ee8921 100644 --- a/src/symbols/acme/chain.rs +++ b/src/symbols/acme/chain.rs @@ -1,66 +1,65 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::fs::File as FsFile; use std::io::Write; -use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct AcmeCertChain<'a, C: 'a + CommandRunner> { - domain: Cow<'a, str>, +pub struct AcmeCertChain<'a, D: AsRef, C: CommandRunner> { + domain: D, command_runner: &'a C, } -impl<'a, C: CommandRunner> AcmeCertChain<'a, C> { - pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, D: AsRef, C: CommandRunner> AcmeCertChain<'a, D, C> { + pub fn new(domain: D, command_runner: &'a C) -> Self { AcmeCertChain { domain, command_runner, } } - fn get_single_cert_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.crt", self.domain) + fn get_single_cert_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into() } - fn get_cert_chain_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.chained.crt", self.domain) + fn get_cert_chain_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into() } } -impl<'a, C: CommandRunner> fmt::Display for AcmeCertChain<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> fmt::Display for AcmeCertChain<'a, D, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AcmeCertChain {}", self.domain) + write!(f, "AcmeCertChain {}", self.domain.as_ref()) } } const DAYS_IN_SECONDS: u32 = 24 * 60 * 60; -impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> Symbol for AcmeCertChain<'a, D, C> { fn target_reached(&self) -> Result> { - if !Path::new(&self.get_cert_chain_path()).exists() { + if !self.get_cert_chain_path().exists() { return Ok(false); } let stdout = self.command_runner.get_output( "openssl", - &[ + args![ "x509", "-in", - &self.get_cert_chain_path(), + self.get_cert_chain_path(), "-noout", "-subject", "-checkend", - &(30 * DAYS_IN_SECONDS).to_string(), + (30 * DAYS_IN_SECONDS).to_string(), ], )?; if stdout != format!( "subject=CN = {}\nCertificate will not expire\n", - self.domain + self.domain.as_ref() ) .as_bytes() { @@ -72,11 +71,11 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> { .command_runner .run_successfully( "openssl", - &[ + args![ "verify", "-untrusted", "/home/acme/lets_encrypt_x3_cross_signed.pem", - &self.get_cert_chain_path(), + self.get_cert_chain_path(), ], ) .is_ok(), @@ -86,8 +85,8 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> { fn execute(&self) -> Result<(), Box> { let output = self.command_runner.get_output( "cat", - &[ - self.get_single_cert_path().as_ref(), + args![ + self.get_single_cert_path(), "/home/acme/lets_encrypt_x3_cross_signed.pem", ], )?; @@ -97,7 +96,10 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> { } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("file", self.get_single_cert_path())] + vec![Resource::new( + "file", + self.get_single_cert_path().to_str().unwrap(), + )] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/acme/user.rs b/src/symbols/acme/user.rs index 927469d..0ba21d5 100644 --- a/src/symbols/acme/user.rs +++ b/src/symbols/acme/user.rs @@ -1,9 +1,4 @@ -use resources::Resource; -use std::borrow::{Borrow, Cow}; -use std::error::Error; -use std::fmt; -use std::ops::Deref; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use command_runner::CommandRunner; use symbols::acme::AcmeAccountKey; @@ -11,72 +6,32 @@ use symbols::dir::Dir; use symbols::file::File; use symbols::list::List; use symbols::owner::Owner; -use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; +use symbols::Symbol; -pub struct AcmeUser<'a>(Cow<'a, str>); - -impl<'a> AcmeUser<'a> { - pub fn new>>(user_name: S) -> Self { - AcmeUser(user_name.into()) - } -} - -impl<'a> Symbol for AcmeUser<'a> { - fn target_reached(&self) -> Result> { - Ok(false) - } - fn execute(&self) -> Result<(), Box> { - Ok(()) - } - fn get_prerequisites(&self) -> Vec { - vec![] - } - fn provides(&self) -> Option> { - None - } - fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { - Box::new(SymbolAction::new(runner, self)) - } - - fn into_action<'b>(self: Box, runner: &'b dyn SymbolRunner) -> Box - where - Self: 'b, - { - Box::new(OwnedSymbolAction::new(runner, *self)) - } -} - -impl<'a> fmt::Display for AcmeUser<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AcmeUser {}", self.0) - } -} - -pub fn new<'a, S: Into>, C: CommandRunner, P: 'a + Deref>( - command_runner: &'a C, - cert: P, - user_name: S, +pub fn new<'a, R: CommandRunner, C: 'a + AsRef, U: 'a + AsRef, H: AsRef>( + command_runner: &'a R, + cert: C, + user_name: U, + home: H, ) -> impl Symbol + 'a { - // impl trait - let user_name_cow = user_name.into(); - let account_key_file: PathBuf = ["/home", user_name_cow.borrow(), "account.key"] - .iter() - .collect(); + let path = |rel: &str| [home.as_ref(), rel.as_ref()].iter().collect::(); + let account_key_file = path("account.key"); List::from(( - AcmeAccountKey::new(account_key_file.clone().into(), command_runner), + AcmeAccountKey::new(account_key_file.clone(), command_runner), + // FIXME into or cow or clone Owner::new( - account_key_file.to_string_lossy().into_owned(), - user_name_cow.clone(), + account_key_file, + user_name.as_ref().to_owned(), command_runner, ), - Dir::new("/home/acme/challenges"), + Dir::new(path("challenges")), Owner::new( - "/home/acme/challenges", - user_name_cow.clone(), + path("challenges"), + user_name.as_ref().to_owned(), command_runner, ), Dir::new("/etc/ssl/local_certs"), - Owner::new("/etc/ssl/local_certs", user_name_cow, command_runner), - File::new("/home/acme/lets_encrypt_x3_cross_signed.pem", cert), + Owner::new("/etc/ssl/local_certs", user_name, command_runner), + File::new(path("lets_encrypt_x3_cross_signed.pem"), cert), )) } diff --git a/src/symbols/cron.rs b/src/symbols/cron.rs index 23ca311..1e68da6 100644 --- a/src/symbols/cron.rs +++ b/src/symbols/cron.rs @@ -1,51 +1,39 @@ use std::error::Error; use std::fmt; -use std::ops::Deref; use command_runner::CommandRunner; -use resources::Resource; + use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct Cron<'r, C, U, R: 'r + CommandRunner> -where - C: Deref, - U: Deref, -{ +pub struct Cron<'r, C: AsRef, U: AsRef, R: CommandRunner> { user: U, content: C, command_runner: &'r R, } -impl<'r, U, R: 'r + CommandRunner> Cron<'r, String, U, R> -where - U: Deref, -{ - pub fn new>(user: U, content: C, command_runner: &'r R) -> Self { +impl<'r, U: AsRef, R: CommandRunner> Cron<'r, String, U, R> { + pub fn new>(user: U, content: C, command_runner: &'r R) -> Self { Cron { user, - content: String::from(&*content) + "\n", + content: String::from(content.as_ref()) + "\n", command_runner, } } } -impl<'r, C, U, R: 'r + CommandRunner> Symbol for Cron<'r, C, U, R> -where - C: Deref, - U: Deref, -{ +impl<'r, C: AsRef, U: AsRef, R: CommandRunner> Symbol for Cron<'r, C, U, R> { fn target_reached(&self) -> Result> { let tab = self .command_runner - .get_output("crontab", &["-l", "-u", &self.user])?; - Ok(tab == self.content.bytes().collect::>()) + .get_output("crontab", args!["-l", "-u", self.user.as_ref(),])?; + Ok(tab == self.content.as_ref().bytes().collect::>()) } fn execute(&self) -> Result<(), Box> { let output = self.command_runner.run_with_args_and_stdin( "crontab", - &["-u", &self.user, "-"], - &self.content, + args!["-u", self.user.as_ref(), "-",], + self.content.as_ref(), )?; if !output.status.success() { return Err(String::from_utf8(output.stderr)?.into()); @@ -53,10 +41,6 @@ where Ok(()) } - fn get_prerequisites(&self) -> Vec { - vec![] - } - fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box { Box::new(SymbolAction::new(runner, self)) } @@ -69,12 +53,8 @@ where } } -impl<'r, C, U, R: 'r + CommandRunner> fmt::Display for Cron<'r, C, U, R> -where - C: Deref, - U: Deref, -{ +impl<'r, C: AsRef, U: AsRef, R: CommandRunner> fmt::Display for Cron<'r, C, U, R> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "Cron {}", &*self.user) + write!(f, "Cron {}", self.user.as_ref()) } } diff --git a/src/symbols/dir.rs b/src/symbols/dir.rs index 7b38b77..d2414f3 100644 --- a/src/symbols/dir.rs +++ b/src/symbols/dir.rs @@ -7,44 +7,29 @@ use std::path::Path; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct Dir -where - D: AsRef, -{ +pub struct Dir> { path: D, } -impl Dir -where - D: AsRef, -{ +impl> Dir { pub fn new(path: D) -> Self { Dir { path } } } -impl Symbol for Dir -where - D: AsRef, -{ +impl> Symbol for Dir { fn target_reached(&self) -> Result> { - let metadata = fs::metadata(self.path.as_ref()); - // Check if dir exists - if let Err(e) = metadata { - return if e.kind() == io::ErrorKind::NotFound { - Ok(false) - } else { - Err(Box::new(e)) - }; + if !self.path.as_ref().exists() { + return Ok(false); } - if metadata.unwrap().is_dir() { - Ok(true) - } else { - Err(Box::new(io::Error::new( + let metadata = fs::metadata(self.path.as_ref())?; + if !metadata.is_dir() { + return Err(Box::new(io::Error::new( io::ErrorKind::AlreadyExists, "Could not create a directory, non-directory file exists", - ))) + ))); } + Ok(true) } fn execute(&self) -> Result<(), Box> { @@ -52,15 +37,18 @@ where } fn get_prerequisites(&self) -> Vec { - if let Some(parent) = Path::new(self.path.as_ref()).parent() { - vec![Resource::new("dir", parent.to_string_lossy())] + if let Some(parent) = self.path.as_ref().parent() { + vec![Resource::new("dir", parent.to_str().unwrap())] } else { vec![] } } fn provides(&self) -> Option> { - Some(vec![Resource::new("dir", self.path.as_ref())]) + Some(vec![Resource::new( + "dir", + self.path.as_ref().to_str().unwrap(), + )]) } fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box { @@ -75,11 +63,8 @@ where } } -impl fmt::Display for Dir -where - D: AsRef, -{ +impl> fmt::Display for Dir { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "Dir {}", self.path.as_ref()) + write!(f, "Dir {}", self.path.as_ref().display()) } } diff --git a/src/symbols/factory.rs b/src/symbols/factory.rs index e18014c..c8e8654 100644 --- a/src/symbols/factory.rs +++ b/src/symbols/factory.rs @@ -1,20 +1,22 @@ use std::borrow::Cow; -use std::ops::Deref; + use std::path::Path; +use std::path::PathBuf; use command_runner::{CommandRunner, SetuidCommandRunner}; use storage::{SimpleStorage, Storage}; -use symbols::acme::{AcmeCert, AcmeCertChain}; +use symbols::acme::{newAcmeUser, AcmeCert, AcmeCertChain}; use symbols::cron::Cron; use symbols::file::File; use symbols::git::checkout::GitCheckout; use symbols::hook::Hook; use symbols::list::List; use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser}; -use symbols::nginx::server::{php_server_config_snippet, server_config, NginxServer}; +use symbols::nginx::server::{server_config, NginxServer}; use symbols::owner::Owner; use symbols::stored_directory::{StorageDirection, StoredDirectory}; use symbols::systemd::reload::ReloadService; +use symbols::systemd::user_service::UserService; use symbols::tls::SelfSignedTlsCert; use symbols::{Symbol, SymbolRunner}; @@ -22,8 +24,11 @@ pub trait Policy { fn user_name_for_host(&self, host_name: &'static str) -> String { host_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part) } - fn home_for_user(&self, user_name: &str) -> String { - format!("/home/{}", user_name) + fn home_for_user(&self, user_name: &str) -> PathBuf { + format!("/home/{}", user_name).into() + } + fn socket_path(&self, user_name: &str, service_name: &str) -> PathBuf { + format!("/var/tmp/{}-{}.socket", user_name, service_name).into() } } @@ -42,7 +47,7 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact pub fn new(command_runner: &'b C, symbol_runner: &'b R, policy: &'b P) -> Self { let acme_user = "acme"; // FIXME: CONFIG - let acme_command_runner = SetuidCommandRunner::new(acme_user, command_runner); + let acme_command_runner = SetuidCommandRunner::new(acme_user.into(), command_runner); SymbolFactory { command_runner, acme_command_runner, @@ -57,14 +62,14 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact nginx_server_symbol: S, ) -> impl Symbol + 'a { List::from(( - SelfSignedTlsCert::new(host.into(), self.command_runner), + SelfSignedTlsCert::new(host, self.command_runner), Hook::new( nginx_server_symbol, ReloadService::new("nginx", self.command_runner), ), - AcmeCert::new(host.into(), &self.acme_command_runner), + AcmeCert::new(host, &self.acme_command_runner), Hook::new( - AcmeCertChain::new(host.into(), &self.acme_command_runner), + AcmeCertChain::new(host, &self.acme_command_runner), ReloadService::new("nginx", self.command_runner), ), )) @@ -79,8 +84,8 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact ) } - fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String { - format!("/run/php/{}.sock", user_name) + fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> PathBuf { + format!("/run/php/{}.sock", user_name).into() } fn get_php_fpm_pool<'a>(&'a self, user_name: &str) -> impl Symbol + 'a { @@ -100,17 +105,18 @@ pm.max_children = 10 catch_workers_output = yes env[PATH] = /usr/local/bin:/usr/bin:/bin ", - user_name, socket + user_name, + socket.to_str().unwrap() ), ), ReloadService::new("php7.0-fpm", self.command_runner), ) } - pub fn serve_php<'a>( + pub fn serve_php<'a, ROOT: AsRef>( &'a self, host_name: &'static str, - root_dir: Cow<'a, str>, + root_dir: ROOT, additional_config: &'a str, ) -> impl Symbol + 'a { let user_name = self.policy.user_name_for_host(host_name); @@ -121,7 +127,7 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin host_name, NginxServer::new_php( host_name, - socket.into(), + socket, root_dir, self.command_runner, additional_config, @@ -130,10 +136,10 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin )) } - pub fn serve_wordpress<'a>( + pub fn serve_wordpress<'a, ROOT: 'a + AsRef>( &'a self, host_name: &'static str, - root_dir: Cow<'a, str>, + root_dir: ROOT, ) -> impl Symbol + 'a { self.serve_php( host_name, @@ -179,16 +185,16 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin }} ", root_dir, - socket)), + socket.to_str().unwrap())), self.command_runner )) )) } - pub fn serve_nextcloud<'a>( + pub fn serve_nextcloud<'a, ROOT: 'a + AsRef>( &'a self, host_name: &'static str, - root_dir: Cow<'a, str>, + root_dir: ROOT, ) -> impl Symbol + 'a { self.serve_php( host_name, @@ -272,13 +278,13 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin let string_target = target.into(); ( StoredDirectory::new( - string_target.clone().into(), + string_target.clone(), data.clone(), StorageDirection::Save, self.command_runner, ), StoredDirectory::new( - string_target.into(), + string_target, data.clone(), StorageDirection::Load, self.command_runner, @@ -290,11 +296,8 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name)); List::from(( MariaDBDatabase::new( - name.into(), - db_dump - .read_filename() - .expect("Initial db dump missing") - .into(), + name, + db_dump.read_filename().expect("Initial db dump missing"), self.command_runner, ), DatabaseDump::new(name, db_dump, self.command_runner), @@ -302,10 +305,10 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin } pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> impl Symbol + 'a { - MariaDBUser::new(user_name.into(), self.command_runner) + MariaDBUser::new(user_name, self.command_runner) } - pub fn get_git_checkout<'a, T: 'a + AsRef>( + pub fn get_git_checkout<'a, T: 'a + AsRef>( &'a self, target: T, source: &'a str, @@ -314,11 +317,15 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin GitCheckout::new(target, source, branch, self.command_runner) } - pub fn get_owner<'a, F: 'a + AsRef>(&'a self, file: F, user: &'a str) -> impl Symbol + 'a { - Owner::new(file, user.into(), self.command_runner) + pub fn get_owner<'a, U: 'a + AsRef, F: 'a + AsRef>( + &'a self, + file: F, + user: U, + ) -> impl Symbol + 'a { + Owner::new(file, user, self.command_runner) } - pub fn get_file<'a, F: 'a + Deref, Q: 'a + AsRef>( + pub fn get_file<'a, F: 'a + AsRef, Q: 'a + AsRef>( &'a self, path: Q, content: F, @@ -326,11 +333,94 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin File::new(path, content) } - pub fn get_cron<'a, T: 'a + Deref, U: 'a + Deref>( + pub fn get_cron<'a, T: 'a + AsRef, U: 'a + AsRef>( &'a self, user: U, content: T, ) -> impl Symbol + 'a { Cron::new(user, content, self.command_runner) } + + pub fn get_acme_user<'a, S: 'a + AsRef, D: 'a + AsRef>( + &'a self, + cert: D, + user_name: S, + ) -> impl Symbol + 'a { + let home = self.policy.home_for_user(user_name.as_ref()); + newAcmeUser(self.command_runner, cert, user_name, home) + } + + pub fn get_systemd_user_service<'a>( + &'a self, + user_name: Cow<'a, str>, + service_name: &'a str, + config: Cow<'a, str>, + ) -> impl Symbol + 'a { + let socket_path = self.policy.socket_path(&user_name, service_name); + let home = self.policy.home_for_user(&user_name); + UserService::new( + socket_path.into(), + home.into(), + user_name, + service_name, + self.command_runner, + config.into(), + ) + } + + fn get_nodejs_systemd_user_service<'a, T: Into>>( + &'a self, + user_name: Cow<'a, str>, + service_name: &'a str, + path: T, + ) -> impl Symbol + 'a { + let socket_path = self.policy.socket_path(&user_name, service_name); + let home = self.policy.home_for_user(&user_name); + UserService::new_nodejs( + home.into(), + user_name, + service_name, + path.into(), + self.command_runner, + socket_path.into(), + ) + } + + pub fn proxy_socket<'a, T: Into>>( + &'a self, + host_name: &'static str, + service_name: &'a str, + root_dir: T, + ) -> impl Symbol + 'a { + let user_name = self.policy.user_name_for_host(host_name); + let socket_path = self.policy.socket_path(&user_name, service_name); + self.get_nginx_acme_server( + host_name, + NginxServer::new_proxy(host_name, socket_path, root_dir.into(), self.command_runner), + ) + } + + pub fn serve_nodejs< + 'a, + 'c: 'a, + S: 'a + Symbol, + T: 'a + Into>, + SP: 'a + Into>, + >( + &'c self, + host: &'static str, + service_name: &'a str, + path: T, + static_path: SP, + nodejs_symbol: S, + ) -> impl Symbol + 'a { + let user_name = self.policy.user_name_for_host(host); + List::from(( + Hook::new( + nodejs_symbol, + self.get_nodejs_systemd_user_service(user_name.into(), service_name, path), + ), + self.proxy_socket(host, service_name, static_path), + )) + } } diff --git a/src/symbols/file.rs b/src/symbols/file.rs index dcc1ce3..1c20183 100644 --- a/src/symbols/file.rs +++ b/src/symbols/file.rs @@ -1,51 +1,34 @@ use std::error::Error; use std::fmt; use std::fs::File as FsFile; -use std::io; + use std::io::{Read, Write}; -use std::ops::Deref; + use std::path::Path; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct File -where - C: Deref, - D: AsRef, -{ +pub struct File, D: AsRef> { path: D, content: C, } -impl File -where - C: Deref, - D: AsRef, -{ +impl, D: AsRef> File { pub fn new(path: D, content: C) -> Self { File { path, content } } } -impl Symbol for File -where - C: Deref, - D: AsRef, -{ +impl, D: AsRef> Symbol for File { fn target_reached(&self) -> Result> { - let file = FsFile::open(self.path.as_ref()); - // Check if file exists - if let Err(e) = file { - return if e.kind() == io::ErrorKind::NotFound { - Ok(false) - } else { - Err(Box::new(e)) - }; + if !self.path.as_ref().exists() { + return Ok(false); } + let file = FsFile::open(self.path.as_ref())?; // Check if content is the same - let mut file_content = file.unwrap().bytes(); - let mut target_content = self.content.bytes(); + let mut file_content = file.bytes(); + let mut target_content = self.content.as_ref().bytes(); loop { match (file_content.next(), target_content.next()) { (None, None) => return Ok(true), @@ -58,18 +41,16 @@ where fn execute(&self) -> Result<(), Box> { let mut file = FsFile::create(self.path.as_ref())?; - file.write_all(self.content.as_bytes())?; + file.write_all(self.content.as_ref().as_bytes())?; Ok(()) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new( - "dir", - Path::new(self.path.as_ref()) - .parent() - .unwrap() - .to_string_lossy(), - )] + if let Some(parent) = self.path.as_ref().parent() { + vec![Resource::new("dir", parent.to_str().unwrap())] + } else { + vec![] + } } fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box { @@ -84,11 +65,7 @@ where } } -impl fmt::Display for File -where - C: Deref, - D: AsRef, -{ +impl, D: AsRef> fmt::Display for File { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "File {}", self.path.as_ref().display()) } diff --git a/src/symbols/git/checkout.rs b/src/symbols/git/checkout.rs index 42e9d7e..88b26b7 100644 --- a/src/symbols/git/checkout.rs +++ b/src/symbols/git/checkout.rs @@ -1,5 +1,7 @@ use std::error::Error; +use std::ffi::OsStr; use std::fmt; +use std::fs::metadata; use std::io; use std::path::Path; @@ -7,14 +9,14 @@ use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct GitCheckout<'a, C: 'a + CommandRunner, T: AsRef> { +pub struct GitCheckout<'a, C: CommandRunner, T: AsRef> { target: T, source: &'a str, branch: &'a str, command_runner: &'a C, } -impl<'a, C: CommandRunner, T: AsRef> GitCheckout<'a, C, T> { +impl<'a, C: CommandRunner, T: AsRef> GitCheckout<'a, C, T> { pub fn new(target: T, source: &'a str, branch: &'a str, command_runner: &'a C) -> Self { GitCheckout { target, @@ -25,29 +27,27 @@ impl<'a, C: CommandRunner, T: AsRef> GitCheckout<'a, C, T> { } } -impl<'a, C: CommandRunner, T: AsRef> fmt::Display for GitCheckout<'a, C, T> { +impl<'a, C: CommandRunner, T: AsRef> fmt::Display for GitCheckout<'a, C, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "Checkout {} (branch {}) into {}", self.source, self.branch, - self.target.as_ref() + self.target.as_ref().display() ) } } -use std::fs::metadata; - -impl<'a, C: CommandRunner, T: AsRef> GitCheckout<'a, C, T> { - fn _run_in_target_repo(&self, args: &[&str]) -> Result, Box> { - let mut new_args = vec!["-C", self.target.as_ref()]; - new_args.extend_from_slice(args); +impl<'a, C: CommandRunner, T: AsRef> GitCheckout<'a, C, T> { + fn _run_in_target_repo>(&self, args: &[S]) -> Result, Box> { + let mut new_args = vec![OsStr::new("-C"), self.target.as_ref().as_ref()]; + new_args.extend(args.iter().map(AsRef::as_ref)); self.command_runner.get_output("git", &new_args) } } -impl<'a, C: CommandRunner, T: AsRef> Symbol for GitCheckout<'a, C, T> { +impl<'a, C: CommandRunner, T: AsRef> Symbol for GitCheckout<'a, C, T> { fn target_reached(&self) -> Result> { if let Err(e) = metadata(self.target.as_ref()) { return if e.kind() == io::ErrorKind::NotFound { @@ -64,17 +64,17 @@ impl<'a, C: CommandRunner, T: AsRef> Symbol for GitCheckout<'a, C, T> { } fn execute(&self) -> Result<(), Box> { - if !Path::new(self.target.as_ref()).exists() { + if !self.target.as_ref().exists() { return self.command_runner.run_successfully( "git", &[ - "clone", - "--depth", - "1", - "-b", - self.branch, - self.source, - self.target.as_ref(), + OsStr::new("clone"), + "--depth".as_ref(), + "1".as_ref(), + "-b".as_ref(), + self.branch.as_ref(), + self.source.as_ref(), + self.target.as_ref().as_ref(), ], ); } @@ -86,15 +86,15 @@ impl<'a, C: CommandRunner, T: AsRef> Symbol for GitCheckout<'a, C, T> { fn get_prerequisites(&self) -> Vec { vec![Resource::new( "dir", - Path::new(self.target.as_ref()) - .parent() - .unwrap() - .to_string_lossy(), + self.target.as_ref().parent().unwrap().to_string_lossy(), )] } fn provides(&self) -> Option> { - Some(vec![Resource::new("dir", self.target.as_ref().to_string())]) + Some(vec![Resource::new( + "dir", + self.target.as_ref().to_str().unwrap(), + )]) } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/git/submodules.rs b/src/symbols/git/submodules.rs index 1adc7fc..81dcad8 100644 --- a/src/symbols/git/submodules.rs +++ b/src/symbols/git/submodules.rs @@ -1,17 +1,18 @@ use std::error::Error; +use std::ffi::OsStr; use std::fmt; use std::path::Path; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct GitSubmodules<'a, C: 'a + CommandRunner> { - target: &'a str, +pub struct GitSubmodules<'a, P: AsRef, C: CommandRunner> { + target: P, command_runner: &'a C, } -impl<'a, C: CommandRunner> GitSubmodules<'a, C> { - pub fn new(target: &'a str, command_runner: &'a C) -> Self { +impl<'a, P: AsRef, C: CommandRunner> GitSubmodules<'a, P, C> { + pub fn new(target: P, command_runner: &'a C) -> Self { GitSubmodules { target, command_runner, @@ -19,26 +20,27 @@ impl<'a, C: CommandRunner> GitSubmodules<'a, C> { } } -impl<'a, C: CommandRunner> fmt::Display for GitSubmodules<'a, C> { +impl<'a, P: AsRef, C: CommandRunner> fmt::Display for GitSubmodules<'a, P, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Submodules for {}", self.target) + write!(f, "Submodules for {}", self.target.as_ref().display()) } } -impl<'a, C: CommandRunner> GitSubmodules<'a, C> { - fn _run_in_target_repo(&self, args: &[&str]) -> Result, Box> { - let mut new_args = vec!["-C", self.target]; +impl<'a, P: AsRef, C: CommandRunner> GitSubmodules<'a, P, C> { + fn _run_in_target_repo(&self, args: &[&OsStr]) -> Result, Box> { + let mut new_args: Vec<&OsStr> = vec![]; + new_args.extend_from_slice(args!["-C", self.target.as_ref()]); new_args.extend_from_slice(args); self.command_runner.get_output("git", &new_args) } } -impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> { +impl<'a, P: AsRef, C: CommandRunner> Symbol for GitSubmodules<'a, P, C> { fn target_reached(&self) -> Result> { - if !Path::new(self.target).exists() { + if !self.target.as_ref().exists() { return Ok(false); } - let output = String::from_utf8(self._run_in_target_repo(&["submodule", "status"])?)?; + let output = String::from_utf8(self._run_in_target_repo(args!["submodule", "status"])?)?; Ok( output .lines() @@ -47,7 +49,7 @@ impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> { } fn execute(&self) -> Result<(), Box> { - self._run_in_target_repo(&["submodule", "update", "--init"])?; + self._run_in_target_repo(args!["submodule", "update", "--init"])?; Ok(()) } diff --git a/src/symbols/mariadb/database.rs b/src/symbols/mariadb/database.rs index 83cf8da..8861adf 100644 --- a/src/symbols/mariadb/database.rs +++ b/src/symbols/mariadb/database.rs @@ -1,18 +1,18 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; +use std::path::Path; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct MariaDBDatabase<'a, C: 'a + CommandRunner> { - db_name: Cow<'a, str>, - seed_file: Cow<'a, str>, +pub struct MariaDBDatabase<'a, D: AsRef, S: AsRef, C: CommandRunner> { + db_name: D, + seed_file: S, command_runner: &'a C, } -impl<'a, C: CommandRunner> MariaDBDatabase<'a, C> { - pub fn new(db_name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, D: AsRef, S: AsRef, C: CommandRunner> MariaDBDatabase<'a, D, S, C> { + pub fn new(db_name: D, seed_file: S, command_runner: &'a C) -> Self { MariaDBDatabase { db_name, seed_file, @@ -23,34 +23,40 @@ impl<'a, C: CommandRunner> MariaDBDatabase<'a, C> { fn run_sql(&self, sql: &str) -> Result> { let b = self .command_runner - .get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?; + .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?; Ok(String::from_utf8(b)?) } } -impl<'a, C: CommandRunner> fmt::Display for MariaDBDatabase<'a, C> { +impl<'a, D: AsRef, S: AsRef, C: CommandRunner> fmt::Display + for MariaDBDatabase<'a, D, S, C> +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "MariaDB Database {}", self.db_name) + write!(f, "MariaDB Database {}", self.db_name.as_ref()) } } -impl<'a, C: CommandRunner> Symbol for MariaDBDatabase<'a, C> { +impl<'a, D: AsRef, S: AsRef, C: CommandRunner> Symbol for MariaDBDatabase<'a, D, S, C> { fn target_reached(&self) -> Result> { Ok( self - .run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name))? + .run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name.as_ref()))? .trim_end() - == self.db_name, + == self.db_name.as_ref(), ) } fn execute(&self) -> Result<(), Box> { - self.run_sql(&format!("CREATE DATABASE {}", self.db_name))?; + self.run_sql(&format!("CREATE DATABASE {}", self.db_name.as_ref()))?; self.command_runner.run_successfully( "sh", - &[ + args![ "-c", - &format!("mariadb '{}' < {}", self.db_name, self.seed_file), + format!( + "mariadb '{}' < {}", + self.db_name.as_ref(), + self.seed_file.as_ref().to_str().unwrap() + ), ], ) } diff --git a/src/symbols/mariadb/database_dump.rs b/src/symbols/mariadb/database_dump.rs index 6577c6a..64d3f22 100644 --- a/src/symbols/mariadb/database_dump.rs +++ b/src/symbols/mariadb/database_dump.rs @@ -6,12 +6,7 @@ use command_runner::CommandRunner; use storage::Storage; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct DatabaseDump<'a, N, C, S> -where - N: 'a + AsRef, - C: 'a + CommandRunner, - S: Storage, -{ +pub struct DatabaseDump<'a, N: AsRef, C: CommandRunner, S: Storage> { db_name: N, storage: S, command_runner: &'a C, @@ -29,7 +24,7 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> fn run_sql(&self, sql: &str) -> Result> { let b = self .command_runner - .get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?; + .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?; Ok(String::from_utf8(b)?) } } @@ -43,7 +38,7 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> fmt::Display for DatabaseD impl<'a, N: AsRef, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a, N, C, S> { fn target_reached(&self) -> Result> { let dump_date = self.storage.recent_date()?; - let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))); + let modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))?; if modified_date.trim_end() == "NULL" { return Ok(false); } @@ -53,9 +48,9 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "sh", - &[ + args![ "-c", - &format!( + format!( "mysqldump '{}' > {}", self.db_name.as_ref(), self.storage.write_filename() diff --git a/src/symbols/mariadb/user.rs b/src/symbols/mariadb/user.rs index ca923ab..9a6d4e0 100644 --- a/src/symbols/mariadb/user.rs +++ b/src/symbols/mariadb/user.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; @@ -6,13 +5,13 @@ use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct MariaDBUser<'a, C: 'a + CommandRunner> { - user_name: Cow<'a, str>, +pub struct MariaDBUser<'a, U: AsRef, C: CommandRunner> { + user_name: U, command_runner: &'a C, } -impl<'a, C: CommandRunner> MariaDBUser<'a, C> { - pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, U: AsRef, C: CommandRunner> MariaDBUser<'a, U, C> { + pub fn new(user_name: U, command_runner: &'a C) -> Self { MariaDBUser { user_name, command_runner, @@ -22,40 +21,40 @@ impl<'a, C: CommandRunner> MariaDBUser<'a, C> { fn run_sql(&self, sql: &str) -> Result> { let b = self .command_runner - .get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?; + .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?; Ok(String::from_utf8(b)?) } } -impl<'a, C: CommandRunner> fmt::Display for MariaDBUser<'a, C> { +impl<'a, U: AsRef, C: CommandRunner> fmt::Display for MariaDBUser<'a, U, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "MariaDB User {}", self.user_name) + write!(f, "MariaDB User {}", self.user_name.as_ref()) } } -impl<'a, C: CommandRunner> Symbol for MariaDBUser<'a, C> { +impl<'a, U: AsRef, C: CommandRunner> Symbol for MariaDBUser<'a, U, C> { fn target_reached(&self) -> Result> { Ok( self .run_sql(&format!( "SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'", - self.user_name + self.user_name.as_ref() ))? .trim_end() - == self.user_name, + == self.user_name.as_ref(), ) } fn execute(&self) -> Result<(), Box> { self.run_sql(&format!( "GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket", - self.user_name + self.user_name.as_ref() ))?; Ok(()) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("user", self.user_name.to_string())] + vec![Resource::new("user", self.user_name.as_ref())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/nginx/server.rs b/src/symbols/nginx/server.rs index 75c67ec..97a82e0 100644 --- a/src/symbols/nginx/server.rs +++ b/src/symbols/nginx/server.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt; use std::io; -use std::ops::Deref; +use std::path::{Path, PathBuf}; use command_runner::CommandRunner; use resources::Resource; @@ -41,16 +41,11 @@ impl fmt::Display for NginxServerError { } } -pub struct NginxServer<'a, C: 'a + CommandRunner, T> -where - T: Deref, -{ +pub struct NginxServer<'a, C: CommandRunner, T: AsRef, P: AsRef> { command_runner: &'a C, - file: FileSymbol, + file: FileSymbol, } -use std::borrow::Cow; - pub fn server_config(domain: &str, content: &str) -> String { format!( "server {{ @@ -80,9 +75,9 @@ server {{ ) } -pub fn php_server_config_snippet<'a>( - socket_path: Cow<'a, str>, - static_path: Cow<'a, str>, +pub fn php_server_config_snippet<'a, SOCKET: AsRef, STATIC: AsRef>( + socket_path: SOCKET, + static_path: STATIC, ) -> String { format!( " @@ -92,7 +87,8 @@ pub fn php_server_config_snippet<'a>( fastcgi_pass unix:{}; include \"snippets/fastcgi-php.conf\"; }}", - static_path, socket_path + static_path.as_ref().to_str().unwrap(), + socket_path.as_ref().to_str().unwrap() ) } @@ -100,9 +96,12 @@ pub trait SocketSpec { fn to_nginx(&self) -> String; } -impl SocketSpec for &str { +impl SocketSpec for T +where + T: AsRef, +{ fn to_nginx(&self) -> String { - format!("unix:{}:", self) + format!("unix:{}:", self.as_ref().to_str().unwrap()) } } @@ -120,7 +119,7 @@ impl SocketSpec for LocalTcpSocket { } } -impl<'a, C: CommandRunner> NginxServer<'a, C, String> { +impl<'a, C: CommandRunner> NginxServer<'a, C, String, PathBuf> { pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self { let content = server_config( domain, @@ -134,10 +133,10 @@ impl<'a, C: CommandRunner> NginxServer<'a, C, String> { NginxServer::new(domain, content, command_runner) } - pub fn new_proxy( + pub fn new_proxy>( domain: &'a str, socket_path: S, - static_path: &'a str, + static_path: STATIC, command_runner: &'a C, ) -> Self { let proxy_content = format!( @@ -160,16 +159,17 @@ location @proxy {{ root {}; {} ", - static_path, proxy_content + static_path.as_ref().to_str().unwrap(), + proxy_content ), ); NginxServer::new(domain, content, command_runner) } - pub fn new_php( + pub fn new_php, STATIC: AsRef>( domain: &'a str, - socket_path: Cow<'a, str>, - static_path: Cow<'a, str>, + socket_path: SOCKET, + static_path: STATIC, command_runner: &'a C, additional_config: &'a str, ) -> Self { @@ -180,7 +180,11 @@ location @proxy {{ NginxServer::new(domain, content, command_runner) } - pub fn new_static(domain: &'a str, static_path: &'a str, command_runner: &'a C) -> Self { + pub fn new_static>( + domain: &'a str, + static_path: S, + command_runner: &'a C, + ) -> Self { let content = server_config( domain, &format!( @@ -188,14 +192,14 @@ location @proxy {{ root {}; try_files $uri $uri/ $uri.html =404; ", - static_path + static_path.as_ref().to_str().unwrap() ), ); NginxServer::new(domain, content, command_runner) } pub fn new(domain: &'a str, content: String, command_runner: &'a C) -> Self { - let file_path = String::from("/etc/nginx/sites-enabled/") + domain; + let file_path: PathBuf = ["/etc/nginx/sites-enabled/", domain].iter().collect(); NginxServer { command_runner, file: FileSymbol::new(file_path, content), @@ -203,10 +207,7 @@ location @proxy {{ } } -impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T> -where - T: Deref, -{ +impl<'a, C: CommandRunner, T: AsRef, P: AsRef> Symbol for NginxServer<'a, C, T, P> { fn target_reached(&self) -> Result> { if !self.file.target_reached()? { return Ok(false); @@ -219,7 +220,7 @@ where self.file.execute()?; self .command_runner - .run_successfully("systemctl", &["reload-or-restart", "nginx"]) + .run_successfully("systemctl", args!["reload-or-restart", "nginx"]) } fn get_prerequisites(&self) -> Vec { @@ -238,9 +239,8 @@ where } } -impl<'a, C: CommandRunner, T> fmt::Display for NginxServer<'a, C, T> -where - T: Deref, +impl<'a, C: CommandRunner, T: AsRef, P: AsRef> fmt::Display + for NginxServer<'a, C, T, P> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Nginx server config") diff --git a/src/symbols/npm.rs b/src/symbols/npm.rs index f573e74..0cfa3e4 100644 --- a/src/symbols/npm.rs +++ b/src/symbols/npm.rs @@ -5,13 +5,13 @@ use std::path::Path; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct NpmInstall<'a, C: 'a + CommandRunner> { - target: &'a str, +pub struct NpmInstall<'a, T: AsRef, C: CommandRunner> { + target: T, command_runner: &'a C, } -impl<'a, C: CommandRunner> NpmInstall<'a, C> { - pub fn new(target: &'a str, command_runner: &'a C) -> Self { +impl<'a, T: AsRef, C: CommandRunner> NpmInstall<'a, T, C> { + pub fn new(target: T, command_runner: &'a C) -> Self { NpmInstall { target, command_runner, @@ -19,20 +19,28 @@ impl<'a, C: CommandRunner> NpmInstall<'a, C> { } } -impl<'a, C: CommandRunner> fmt::Display for NpmInstall<'a, C> { +impl<'a, T: AsRef, C: CommandRunner> fmt::Display for NpmInstall<'a, T, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "npm install in {}", self.target) + write!( + f, + "npm install in {}", + self.target.as_ref().to_str().unwrap() + ) } } -impl<'a, C: CommandRunner> Symbol for NpmInstall<'a, C> { +impl<'a, T: AsRef, C: CommandRunner> Symbol for NpmInstall<'a, T, C> { fn target_reached(&self) -> Result> { - if !Path::new(self.target).exists() { + if !self.target.as_ref().exists() { return Ok(false); } - let result = self - .command_runner - .run_with_args("sh", &["-c", &format!("cd '{}' && npm ls", self.target)])?; + let result = self.command_runner.run_with_args( + "sh", + args![ + "-c", + format!("cd '{}' && npm ls", self.target.as_ref().to_str().unwrap()), + ], + )?; Ok( result.status.success() && !String::from_utf8(result.stdout) @@ -44,11 +52,11 @@ impl<'a, C: CommandRunner> Symbol for NpmInstall<'a, C> { fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "sh", - &[ + args![ "-c", - &format!( + format!( "cd '{}' && npm install --production --unsafe-perm", - self.target + self.target.as_ref().to_str().unwrap() ), ], ) diff --git a/src/symbols/owner.rs b/src/symbols/owner.rs index 29d7c5a..6196a62 100644 --- a/src/symbols/owner.rs +++ b/src/symbols/owner.rs @@ -1,5 +1,6 @@ -use std::borrow::{Borrow, Cow}; + use std::error::Error; + use std::fmt; use std::fs; use std::os::unix::fs::MetadataExt; @@ -11,20 +12,14 @@ use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct Owner<'a, C: 'a + CommandRunner, D> -where - D: AsRef, -{ +pub struct Owner<'a, C: CommandRunner, D: AsRef, U: AsRef> { path: D, - user_name: Cow<'a, str>, + user_name: U, command_runner: &'a C, } -impl<'a, C: CommandRunner, D> Owner<'a, C, D> -where - D: AsRef, -{ - pub fn new(path: D, user_name: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, C: CommandRunner, D: AsRef, U: AsRef> Owner<'a, C, D, U> { + pub fn new(path: D, user_name: U, command_runner: &'a C) -> Self { Owner { path, user_name, @@ -33,28 +28,25 @@ where } } -impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D> -where - D: AsRef, -{ +impl<'a, C: CommandRunner, D: AsRef, U: AsRef> Symbol for Owner<'a, C, D, U> { fn target_reached(&self) -> Result> { if !Path::new(self.path.as_ref()).exists() { return Ok(false); } let actual_uid = fs::metadata(self.path.as_ref()).unwrap().uid(); - let target_uid = get_user_by_name(self.user_name.borrow()).unwrap().uid(); + let target_uid = get_user_by_name(self.user_name.as_ref()).unwrap().uid(); Ok(actual_uid == target_uid) } fn execute(&self) -> Result<(), Box> { - self.command_runner.run_successfully( - "chown", - &["-R", self.user_name.borrow(), self.path.as_ref()], - ) + let uname: &str = self.user_name.as_ref(); + self + .command_runner + .run_successfully("chown", args!["-R", uname, self.path.as_ref(),]) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("user", self.user_name.to_string())] + vec![Resource::new("user", self.user_name.as_ref())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { @@ -69,11 +61,13 @@ where } } -impl<'a, C: CommandRunner, D> fmt::Display for Owner<'a, C, D> -where - D: AsRef, -{ +impl<'a, C: CommandRunner, D: AsRef, U: AsRef> fmt::Display for Owner<'a, C, D, U> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "Owner {} for {}", self.user_name, self.path.as_ref()) + write!( + f, + "Owner {} for {}", + self.user_name.as_ref(), + self.path.as_ref().display() + ) } } diff --git a/src/symbols/postgresql/database.rs b/src/symbols/postgresql/database.rs index 7439d12..57e6653 100644 --- a/src/symbols/postgresql/database.rs +++ b/src/symbols/postgresql/database.rs @@ -1,18 +1,17 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct PostgreSQLDatabase<'a, C: 'a + CommandRunner> { - name: Cow<'a, str>, - seed_file: Cow<'a, str>, +pub struct PostgreSQLDatabase<'a, N: AsRef, S: AsRef, C: CommandRunner> { + name: N, + seed_file: S, command_runner: &'a C, } -impl<'a, C: CommandRunner> PostgreSQLDatabase<'a, C> { - pub fn new(name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, N: AsRef, S: AsRef, C: CommandRunner> PostgreSQLDatabase<'a, N, S, C> { + pub fn new(name: N, seed_file: S, command_runner: &'a C) -> Self { PostgreSQLDatabase { name, seed_file, @@ -23,52 +22,68 @@ impl<'a, C: CommandRunner> PostgreSQLDatabase<'a, C> { fn run_sql(&self, sql: &str) -> Result> { let b = self.command_runner.get_output( "su", - &["-", "postgres", "-c", &format!("psql -t -c \"{}\"", sql)], + args!["-", "postgres", "-c", format!("psql -t -c \"{}\"", sql)], )?; Ok(String::from_utf8(b)?) } } -impl<'a, C: CommandRunner> fmt::Display for PostgreSQLDatabase<'a, C> { +impl<'a, N: AsRef, S: AsRef, C: CommandRunner> fmt::Display + for PostgreSQLDatabase<'a, N, S, C> +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "PostgreSQL Database {}", self.name) + write!(f, "PostgreSQL Database {}", self.name.as_ref()) } } -impl<'a, C: CommandRunner> Symbol for PostgreSQLDatabase<'a, C> { +impl<'a, N: AsRef, S: AsRef, C: CommandRunner> Symbol + for PostgreSQLDatabase<'a, N, S, C> +{ fn target_reached(&self) -> Result> { Ok( self .run_sql(&format!( "SELECT datname FROM pg_database WHERE datname LIKE '{}'", - self.name + self.name.as_ref() ))? .trim() - == self.name, + == self.name.as_ref(), ) } fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "su", - &["-", "postgres", "-c", &format!("createuser {}", self.name)], + args![ + "-", + "postgres", + "-c", + format!("createuser {}", self.name.as_ref()) + ], )?; self.command_runner.run_successfully( "su", - &[ + args![ "-", "postgres", "-c", - &format!("createdb -E UTF8 -T template0 -O {} {0}", self.name), + format!( + "createdb -E UTF8 -T template0 -O {} {0}", + self.name.as_ref() + ), ], )?; self.command_runner.run_successfully( "su", - &[ + args![ "-", "postgres", "-c", - &format!("psql '{}' < {}", self.name, self.seed_file), + format!( + "psql '{}' < {}", + self.name.as_ref(), + self.seed_file.as_ref() + ), ], ) } diff --git a/src/symbols/postgresql/database_dump.rs b/src/symbols/postgresql/database_dump.rs index 6577c6a..64d3f22 100644 --- a/src/symbols/postgresql/database_dump.rs +++ b/src/symbols/postgresql/database_dump.rs @@ -6,12 +6,7 @@ use command_runner::CommandRunner; use storage::Storage; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct DatabaseDump<'a, N, C, S> -where - N: 'a + AsRef, - C: 'a + CommandRunner, - S: Storage, -{ +pub struct DatabaseDump<'a, N: AsRef, C: CommandRunner, S: Storage> { db_name: N, storage: S, command_runner: &'a C, @@ -29,7 +24,7 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> fn run_sql(&self, sql: &str) -> Result> { let b = self .command_runner - .get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?; + .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?; Ok(String::from_utf8(b)?) } } @@ -43,7 +38,7 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> fmt::Display for DatabaseD impl<'a, N: AsRef, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a, N, C, S> { fn target_reached(&self) -> Result> { let dump_date = self.storage.recent_date()?; - let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))); + let modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))?; if modified_date.trim_end() == "NULL" { return Ok(false); } @@ -53,9 +48,9 @@ impl<'a, N: AsRef, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "sh", - &[ + args![ "-c", - &format!( + format!( "mysqldump '{}' > {}", self.db_name.as_ref(), self.storage.write_filename() diff --git a/src/symbols/stored_directory.rs b/src/symbols/stored_directory.rs index 7ed933f..9ad5d18 100644 --- a/src/symbols/stored_directory.rs +++ b/src/symbols/stored_directory.rs @@ -1,4 +1,3 @@ -use std::borrow::{Borrow, Cow}; use std::error::Error; use std::fmt; use std::fs; @@ -17,21 +16,15 @@ pub enum StorageDirection { Save, } -pub struct StoredDirectory<'a, S, C: 'a + CommandRunner> -where - S: Storage, -{ - path: Cow<'a, str>, +pub struct StoredDirectory<'a, P: AsRef, S: Storage, C: CommandRunner> { + path: P, storage: S, dir: StorageDirection, command_runner: &'a C, } -impl<'a, S, C: CommandRunner> StoredDirectory<'a, S, C> -where - S: Storage, -{ - pub fn new(path: Cow<'a, str>, storage: S, dir: StorageDirection, command_runner: &'a C) -> Self { +impl<'a, P: AsRef, S: Storage, C: CommandRunner> StoredDirectory<'a, P, S, C> { + pub fn new(path: P, storage: S, dir: StorageDirection, command_runner: &'a C) -> Self { StoredDirectory { path, storage, @@ -41,19 +34,20 @@ where } } -impl<'a, S, C: CommandRunner> fmt::Display for StoredDirectory<'a, S, C> -where - S: Storage, +impl<'a, P: AsRef, S: Storage, C: CommandRunner> fmt::Display + for StoredDirectory<'a, P, S, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Stored directory {} ({:?})", self.path, self.dir) + write!( + f, + "Stored directory {} ({:?})", + self.path.as_ref().display(), + self.dir + ) } } -impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C> -where - S: Storage, -{ +impl<'a, P: AsRef, S: Storage, C: CommandRunner> Symbol for StoredDirectory<'a, P, S, C> { fn target_reached(&self) -> Result> { let metadata = fs::metadata(self.path.as_ref()); // Check if dir exists @@ -74,11 +68,11 @@ where let dump_date = self.storage.recent_date()?; let output = self.command_runner.get_output( "sh", - &[ + args![ "-c", - &format!( + format!( "find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o", - self.path + self.path.as_ref().to_str().unwrap() ), ], )?; @@ -90,7 +84,7 @@ where } { let output = self.command_runner.run_with_args( "diff", - &["-rq", &self.storage.read_filename()?, self.path.borrow()], + args!["-rq", self.storage.read_filename()?, self.path.as_ref()], )?; match output.status.code() { Some(0) => Ok(true), @@ -106,15 +100,15 @@ where if self.dir == StorageDirection::Load { self .command_runner - .run_successfully("rm", &["-rf", self.path.borrow()])?; + .run_successfully("rm", args!["-rf", self.path.as_ref()])?; self.command_runner.run_successfully( "cp", - &["-a", &self.storage.read_filename()?, self.path.borrow()], + args!["-a", self.storage.read_filename()?, self.path.as_ref()], ) } else { self.command_runner.run_successfully( "cp", - &["-a", self.path.borrow(), &self.storage.write_filename()], + args!["-a", self.path.as_ref(), self.storage.write_filename()], ) } } @@ -123,8 +117,8 @@ where if self.dir == StorageDirection::Save { return vec![]; } - if let Some(parent) = Path::new(self.path.as_ref()).parent() { - vec![Resource::new("dir", parent.to_string_lossy())] + if let Some(parent) = self.path.as_ref().parent() { + vec![Resource::new("dir", parent.to_str().unwrap())] } else { vec![] } @@ -132,7 +126,10 @@ where fn provides(&self) -> Option> { if self.dir == StorageDirection::Load { - Some(vec![Resource::new("dir", self.path.to_string())]) + Some(vec![Resource::new( + "dir", + self.path.as_ref().to_str().unwrap(), + )]) } else { None } diff --git a/src/symbols/systemd/reload.rs b/src/symbols/systemd/reload.rs index 106df23..650ab94 100644 --- a/src/symbols/systemd/reload.rs +++ b/src/symbols/systemd/reload.rs @@ -26,7 +26,7 @@ impl<'a, C: CommandRunner> Symbol for ReloadService<'a, C> { fn execute(&self) -> Result<(), Box> { self .command_runner - .run_successfully("systemctl", &["reload-or-restart", self.service]) + .run_successfully("systemctl", args!["reload-or-restart", self.service]) } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/systemd/user_service.rs b/src/symbols/systemd/user_service.rs index 72a2d58..fa976e9 100644 --- a/src/symbols/systemd/user_service.rs +++ b/src/symbols/systemd/user_service.rs @@ -1,8 +1,12 @@ +use std::borrow::Cow; use std::error::Error; +use std::ffi::OsStr; use std::fmt; use std::fs; use std::io; -use std::ops::Deref; + +use std::path::Path; +use std::path::PathBuf; use std::process::Output; use std::thread::sleep; use std::time::Duration; @@ -51,29 +55,23 @@ impl fmt::Display for UserServiceError { } } -pub struct UserService<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ +pub struct UserService<'a, C: AsRef, R: CommandRunner> { + socket_path: Cow<'a, Path>, service_name: &'a str, - user_name: &'a str, + user_name: Cow<'a, str>, command_runner: R, - file: FileSymbol, + config: FileSymbol, } -impl<'a, R> UserService<'a, String, SetuidCommandRunner<'a, R>> -where - R: CommandRunner, -{ - pub fn new_nodejs( - home: &'a str, - user_name: &'a str, +impl<'a, R: CommandRunner> UserService<'a, String, SetuidCommandRunner<'a, R>> { + pub fn new_nodejs<'b: 'a>( + home: Cow<'a, Path>, + user_name: Cow<'a, str>, service_name: &'a str, - path: &'a str, + path: Cow<'a, Path>, command_runner: &'a R, + socket_path: Cow<'a, Path>, ) -> Self { - let port = format!("/var/tmp/{}-{}.socket", user_name, service_name); let content = format!( "[Service] Environment=NODE_ENV=production @@ -91,39 +89,46 @@ Restart=always [Install] WantedBy=default.target ", - path, port + path.to_str().unwrap(), + socket_path.to_str().unwrap() ); - UserService::new(home, user_name, service_name, command_runner, content) + UserService::new( + socket_path, + home, + user_name, + service_name, + command_runner, + content, + ) } pub fn new( - home: &'a str, - user_name: &'a str, + socket_path: Cow<'a, Path>, + home: Cow<'a, Path>, + user_name: Cow<'a, str>, service_name: &'a str, command_runner: &'a R, content: String, ) -> Self { - let file_path = format!( - "{}/.config/systemd/user/{}.service", - home.trim_end(), - service_name - ); + let config_path: PathBuf = [ + home.as_ref(), + format!(".config/systemd/user/{}.service", service_name).as_ref(), + ] + .iter() + .collect(); UserService { + socket_path, service_name, - user_name, + user_name: user_name.clone(), command_runner: SetuidCommandRunner::new(user_name, command_runner), - file: FileSymbol::new(file_path, content), + config: FileSymbol::new(config_path, content), } } } -impl<'a, C, R> UserService<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ - fn systemctl_wait_for_dbus(&self, args: &[&str]) -> Result> { +impl<'a, C: AsRef, R: CommandRunner> UserService<'a, C, R> { + fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result> { let mut tries = 5; loop { let result = self.command_runner.run_with_args("systemctl", args)?; @@ -146,7 +151,7 @@ where fn check_if_service(&self) -> Result> { loop { - let active_state = self.systemctl_wait_for_dbus(&[ + let active_state = self.systemctl_wait_for_dbus(args![ "--user", "show", "--property", @@ -160,7 +165,7 @@ where return Err(Box::new( UserServiceError::ActivationFailed(self.command_runner.run_with_args( "journalctl", - &["--user", &format!("--user-unit={}", self.service_name)], + args!["--user", format!("--user-unit={}", self.service_name)], )) as UserServiceError, )) } @@ -170,22 +175,18 @@ where } } -impl<'a, C, R> Symbol for UserService<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ +impl<'a, C: AsRef, R: CommandRunner> Symbol for UserService<'a, C, R> { fn target_reached(&self) -> Result> { - if !(self.file.target_reached()?) { + if !(self.config.target_reached()?) { return Ok(false); } self.check_if_service() } fn execute(&self) -> Result<(), Box> { - self.file.execute()?; - self.systemctl_wait_for_dbus(&["--user", "enable", self.service_name])?; - self.systemctl_wait_for_dbus(&["--user", "restart", self.service_name])?; + self.config.execute()?; + self.systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])?; + self.systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])?; if !(self.check_if_service()?) { return Err(Box::new( @@ -193,8 +194,7 @@ where )); } - let file_name = format!("/var/tmp/{}-{}.socket", self.user_name, self.service_name); - fs::metadata(&file_name) + fs::metadata(&self.socket_path) .map(|_| ()) .map_err(|e| Box::new(e) as Box) } @@ -204,7 +204,7 @@ where "file", format!("/var/lib/systemd/linger/{}", self.user_name), )]; - r.extend(self.file.get_prerequisites().into_iter()); + r.extend(self.config.get_prerequisites().into_iter()); r } @@ -220,11 +220,7 @@ where } } -impl<'a, C, R> fmt::Display for UserService<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ +impl<'a, C: AsRef, R: CommandRunner> fmt::Display for UserService<'a, C, R> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Systemd user service unit for {}", self.service_name) } diff --git a/src/symbols/systemd/user_session.rs b/src/symbols/systemd/user_session.rs index 92c7e78..602403d 100644 --- a/src/symbols/systemd/user_session.rs +++ b/src/symbols/systemd/user_session.rs @@ -1,4 +1,3 @@ -use std::borrow::{Borrow, Cow}; use std::error::Error; use std::fmt; use std::path::PathBuf; @@ -33,13 +32,13 @@ impl fmt::Display for SystemdUserSessionError { } } -pub struct SystemdUserSession<'a, C: 'a + CommandRunner> { - user_name: Cow<'a, str>, +pub struct SystemdUserSession<'a, U: AsRef, C: CommandRunner> { + user_name: U, command_runner: &'a C, } -impl<'a, C: CommandRunner> SystemdUserSession<'a, C> { - pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, U: AsRef, C: CommandRunner> SystemdUserSession<'a, U, C> { + pub fn new(user_name: U, command_runner: &'a C) -> Self { SystemdUserSession { user_name, command_runner, @@ -47,10 +46,10 @@ impl<'a, C: CommandRunner> SystemdUserSession<'a, C> { } } -impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> { +impl<'a, U: AsRef, C: CommandRunner> Symbol for SystemdUserSession<'a, U, C> { fn target_reached(&self) -> Result> { let mut path = PathBuf::from("/var/lib/systemd/linger"); - path.push(self.user_name.borrow() as &str); + path.push(self.user_name.as_ref()); Ok(path.exists()) // Could also do `loginctl show-user ${self.user_name} | grep -F 'Linger=yes` } @@ -58,7 +57,7 @@ impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> { fn execute(&self) -> Result<(), Box> { self .command_runner - .run_successfully("loginctl", &["enable-linger", self.user_name.borrow()]) + .run_successfully("loginctl", args!["enable-linger", self.user_name.as_ref()]) } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { @@ -73,8 +72,8 @@ impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> { } } -impl<'a, C: CommandRunner> fmt::Display for SystemdUserSession<'a, C> { +impl<'a, U: AsRef, C: CommandRunner> fmt::Display for SystemdUserSession<'a, U, C> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "Systemd user session for {}", self.user_name) + write!(f, "Systemd user session for {}", self.user_name.as_ref()) } } diff --git a/src/symbols/tls/csr.rs b/src/symbols/tls/csr.rs index 4df226f..de9681c 100644 --- a/src/symbols/tls/csr.rs +++ b/src/symbols/tls/csr.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use std::error::Error; +use std::ffi::OsStr; use std::fmt; -use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use resources::Resource; @@ -20,12 +21,12 @@ impl<'a, C: CommandRunner> TlsCsr<'a, C> { } } - fn get_key_path(&self) -> String { - format!("/etc/ssl/private/{}.key", self.domain) + fn get_key_path(&self) -> PathBuf { + format!("/etc/ssl/private/{}.key", self.domain).into() } - fn get_csr_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.csr", self.domain) + fn get_csr_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.csr", self.domain).into() } } @@ -37,13 +38,19 @@ impl<'a, C: CommandRunner> fmt::Display for TlsCsr<'a, C> { impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> { fn target_reached(&self) -> Result> { - if !Path::new(&self.get_csr_path()).exists() { + if !self.get_csr_path().exists() { return Ok(false); } let output = self.command_runner.get_stderr( "openssl", - &["req", "-in", &self.get_csr_path(), "-noout", "-verify"], + &[ + OsStr::new("req"), + "-in".as_ref(), + self.get_csr_path().as_ref(), + "-noout".as_ref(), + "-verify".as_ref(), + ], )?; Ok(output == b"verify OK\n") } @@ -52,22 +59,22 @@ impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> { self.command_runner.run_successfully( "openssl", &[ - "req", - "-new", - "-sha256", - "-key", - &self.get_key_path(), - "-out", - &self.get_csr_path(), - "-subj", - &format!("/CN={}", self.domain), + OsStr::new("req"), + "-new".as_ref(), + "-sha256".as_ref(), + "-key".as_ref(), + self.get_key_path().as_ref(), + "-out".as_ref(), + self.get_csr_path().as_ref(), + "-subj".as_ref(), + format!("/CN={}", self.domain).as_ref(), ], )?; Ok(()) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("file", self.get_key_path())] + vec![Resource::new("file", self.get_key_path().to_str().unwrap())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/tls/key.rs b/src/symbols/tls/key.rs index d347963..f25c754 100644 --- a/src/symbols/tls/key.rs +++ b/src/symbols/tls/key.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use std::error::Error; +use std::ffi::OsStr; use std::fmt; -use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; @@ -19,8 +20,10 @@ impl<'a, C: CommandRunner> TlsKey<'a, C> { } } - fn get_path(&self) -> String { - format!("/etc/ssl/private/{}.key", self.domain) + fn get_path(&self) -> PathBuf { + ["/etc/ssl/private", &format!("{}.key", self.domain)] + .iter() + .collect() } fn get_bytes(&self) -> u32 { @@ -36,13 +39,20 @@ impl<'a, C: CommandRunner> fmt::Display for TlsKey<'a, C> { impl<'a, C: CommandRunner> Symbol for TlsKey<'a, C> { fn target_reached(&self) -> Result> { - if !Path::new(&self.get_path()).exists() { + if !self.get_path().exists() { return Ok(false); } let output = self.command_runner.get_output( "openssl", - &["rsa", "-in", &self.get_path(), "-noout", "-check", "-text"], + &[ + OsStr::new("rsa"), + "-in".as_ref(), + self.get_path().as_ref(), + "-noout".as_ref(), + "-check".as_ref(), + "-text".as_ref(), + ], )?; Ok(output.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes())) } @@ -51,10 +61,10 @@ impl<'a, C: CommandRunner> Symbol for TlsKey<'a, C> { self.command_runner.run_successfully( "openssl", &[ - "genrsa", - "-out", - &self.get_path(), - &self.get_bytes().to_string(), + OsStr::new("genrsa"), + "-out".as_ref(), + self.get_path().as_ref(), + self.get_bytes().to_string().as_ref(), ], ) } diff --git a/src/symbols/tls/self_signed_cert.rs b/src/symbols/tls/self_signed_cert.rs index cf09fea..059c154 100644 --- a/src/symbols/tls/self_signed_cert.rs +++ b/src/symbols/tls/self_signed_cert.rs @@ -1,57 +1,56 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; -use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct SelfSignedTlsCert<'a, C: 'a + CommandRunner> { - domain: Cow<'a, str>, +pub struct SelfSignedTlsCert<'a, D: AsRef, C: CommandRunner> { + domain: D, command_runner: &'a C, } -impl<'a, C: CommandRunner> SelfSignedTlsCert<'a, C> { - pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self { +impl<'a, D: AsRef, C: CommandRunner> SelfSignedTlsCert<'a, D, C> { + pub fn new(domain: D, command_runner: &'a C) -> Self { SelfSignedTlsCert { domain, command_runner, } } - fn get_key_path(&self) -> String { - format!("/etc/ssl/private/{}.key", self.domain) + fn get_key_path(&self) -> PathBuf { + format!("/etc/ssl/private/{}.key", self.domain.as_ref()).into() } - fn get_cert_path(&self) -> String { - format!("/etc/ssl/local_certs/{}.chained.crt", self.domain) + fn get_cert_path(&self) -> PathBuf { + format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into() } } -impl<'a, C: CommandRunner> fmt::Display for SelfSignedTlsCert<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> fmt::Display for SelfSignedTlsCert<'a, D, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "SelfSignedTlsCert {}", self.domain) + write!(f, "SelfSignedTlsCert {}", self.domain.as_ref()) } } const DAYS_IN_SECONDS: u32 = 24 * 60 * 60; -impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> { +impl<'a, D: AsRef, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, D, C> { fn target_reached(&self) -> Result> { - if !Path::new(&self.get_cert_path()).exists() { + if !self.get_cert_path().exists() { return Ok(false); } let output = self.command_runner.run_with_args( "openssl", - &[ + args![ "x509", "-in", - &self.get_cert_path(), + self.get_cert_path(), "-noout", "-subject", "-checkend", - &(30 * DAYS_IN_SECONDS).to_string(), + (30 * DAYS_IN_SECONDS).to_string(), ], )?; println!("{}", output.status.code().unwrap()); @@ -60,13 +59,17 @@ impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> { output.stdout == format!( "subject=CN = {}\nCertificate will not expire\n", - self.domain + self.domain.as_ref() ) .as_bytes(), ), Some(_) => { if output.stdout - == format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes() + == format!( + "subject=CN = {}\nCertificate will expire\n", + self.domain.as_ref() + ) + .as_bytes() { Ok(false) } else { @@ -80,24 +83,24 @@ impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> { fn execute(&self) -> Result<(), Box> { self.command_runner.run_successfully( "openssl", - &[ + args![ "req", "-x509", "-sha256", "-days", "90", "-key", - &self.get_key_path(), + self.get_key_path(), "-out", - &self.get_cert_path(), + self.get_cert_path(), "-subj", - &format!("/CN={}", self.domain), + format!("/CN={}", self.domain.as_ref()), ], ) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("file", self.get_key_path())] + vec![Resource::new("file", self.get_key_path().to_str().unwrap())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { diff --git a/src/symbols/user.rs b/src/symbols/user.rs index f2524c5..4b29501 100644 --- a/src/symbols/user.rs +++ b/src/symbols/user.rs @@ -69,7 +69,7 @@ impl fmt::Display for UserError { } } -pub struct User<'a, C: 'a + CommandRunner, A: 'a + UserAdder> { +pub struct User<'a, C: CommandRunner, A: UserAdder> { user_name: Cow<'a, str>, command_runner: &'a C, user_adder: &'a A, @@ -95,7 +95,7 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> { fn target_reached(&self) -> Result> { let output = self .command_runner - .run_with_args("getent", &["passwd", &*self.user_name])?; + .run_with_args("getent", args!["passwd", self.user_name.as_ref()])?; match output.status.code() { Some(2) => Ok(false), Some(0) => Ok(true), @@ -106,12 +106,12 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> { fn execute(&self) -> Result<(), Box> { self .user_adder - .add_user(&*self.user_name) + .add_user(self.user_name.as_ref()) .map_err(|e| Box::new(e) as Box) } fn provides(&self) -> Option> { - Some(vec![Resource::new("user", self.user_name.to_string())]) + Some(vec![Resource::new("user", self.user_name.to_owned())]) } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { @@ -140,7 +140,7 @@ impl<'a, C: CommandRunner> UserAdder for SystemUserAdder<'a, C> { fn add_user(&self, user_name: &str) -> Result<(), UserAdderError> { let output = self.command_runner.run_with_args( "adduser", - &[ + args![ // "-m", // Necessary for Fedora, not accepted in Debian "--system", user_name, ], diff --git a/src/symbols/wordpress/plugin.rs b/src/symbols/wordpress/plugin.rs index 0f2fe79..23230c0 100644 --- a/src/symbols/wordpress/plugin.rs +++ b/src/symbols/wordpress/plugin.rs @@ -4,29 +4,20 @@ use std::fmt; use std::fs::File as FsFile; use std::io; use std::io::{BufRead, BufReader}; -use std::ops::Deref; use std::path::{Path, PathBuf}; use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct WordpressPlugin<'a, C, R> -where - C: Deref, - R: 'a + CommandRunner, -{ - base: C, - name: C, +pub struct WordpressPlugin<'a, P: AsRef, N: AsRef, R: CommandRunner> { + base: P, + name: N, command_runner: &'a R, } -impl<'a, C, R> WordpressPlugin<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ - pub fn new(base: C, name: C, command_runner: &'a R) -> Self { +impl<'a, P: AsRef, N: AsRef, R: CommandRunner> WordpressPlugin<'a, P, N, R> { + pub fn new(base: P, name: N, command_runner: &'a R) -> Self { WordpressPlugin { base, name, @@ -35,22 +26,21 @@ where } fn get_path(&self) -> PathBuf { - Path::new(&*self.base) + self + .base + .as_ref() .join("wp-content/plugins") - .join(&*self.name) + .join(self.name.as_ref()) } } -impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R> -where - C: Deref, - R: CommandRunner, -{ +impl<'a, P: AsRef, N: AsRef, R: CommandRunner> Symbol for WordpressPlugin<'a, P, N, R> { fn target_reached(&self) -> Result> { - if !self.get_path().exists() { + let base_path = self.get_path(); + if !base_path.exists() { return Ok(false); } - let path = self.get_path().join(self.name.to_string() + ".php"); + let path = base_path.join(self.name.as_ref().to_owned() + ".php"); let mut version = String::new(); let mut plugin_uri = String::new(); match FsFile::open(path) { @@ -78,11 +68,13 @@ where } let upstream = self.command_runner.get_output( "curl", - &[ + args![ "--form", - &format!( + format!( r###"plugins={{"plugins":{{"{0}/{0}.php":{{"Version":"{1}", "PluginURI":"{2}"}}}}}}"###, - &*self.name, version, plugin_uri + self.name.as_ref(), + version, + plugin_uri ), "https://api.wordpress.org/plugins/update-check/1.1/", ], @@ -91,23 +83,20 @@ where } fn execute(&self) -> Result<(), Box> { - let source = format!("https://downloads.wordpress.org/plugin/{}.zip", &*self.name); - let zip = format!("/tmp/{}.zip", &*self.name); + let source = format!( + "https://downloads.wordpress.org/plugin/{}.zip", + self.name.as_ref() + ); + let zip = format!("/tmp/{}.zip", self.name.as_ref()); self .command_runner - .run_successfully("curl", &[source.as_ref() as &str, "-o", zip.as_ref()])?; + .run_successfully("curl", args![source, "-o", zip])?; self .command_runner - .run_successfully("rm", &["-rf", &self.get_path().to_string_lossy()])?; + .run_successfully("rm", args!["-rf", self.get_path()])?; self.command_runner.run_successfully( "unzip", - &[ - zip.as_ref(), - "-d".as_ref(), - Path::new(&*self.base) - .join("wp-content/plugins") - .as_os_str(), - ], + args![zip, "-d", self.base.as_ref().join("wp-content/plugins")], ) } @@ -130,12 +119,10 @@ where } } -impl<'a, C, R> fmt::Display for WordpressPlugin<'a, C, R> -where - C: Deref, - R: CommandRunner, +impl<'a, P: AsRef, N: AsRef, R: CommandRunner> fmt::Display + for WordpressPlugin<'a, P, N, R> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "WordpressPlugin {}", &*self.name) + write!(f, "WordpressPlugin {}", self.name.as_ref()) } } diff --git a/src/symbols/wordpress/translation.rs b/src/symbols/wordpress/translation.rs index dcf6f22..469406d 100644 --- a/src/symbols/wordpress/translation.rs +++ b/src/symbols/wordpress/translation.rs @@ -1,39 +1,31 @@ use regex::Regex; use std::cmp::max; use std::error::Error; + use std::fmt; use std::fs::File as FsFile; use std::io; use std::io::{BufRead, BufReader}; use std::path::Path; +use std::path::PathBuf; use command_runner::CommandRunner; use resources::Resource; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; -pub struct WordpressTranslation<'a, C, D, R> -where - C: AsRef, - D: AsRef, - R: 'a + CommandRunner, -{ +pub struct WordpressTranslation<'a, C: AsRef, D: AsRef, R: CommandRunner> { path: D, version: &'a str, locale: C, command_runner: &'a R, } -impl<'a, C, R> WordpressTranslation<'a, C, String, R> -where - C: AsRef, - R: CommandRunner, -{ - pub fn new>(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self { +impl<'a, C: AsRef, R: CommandRunner> WordpressTranslation<'a, C, PathBuf, R> { + pub fn new>(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self { WordpressTranslation { - path: Path::new(path.as_ref()) - .join("wp-content/languages") - .to_string_lossy() - .to_string(), + path: [path.as_ref(), "wp-content/languages".as_ref()] + .iter() + .collect(), version, locale, command_runner, @@ -41,19 +33,14 @@ where } } -impl<'a, C, D, R> WordpressTranslation<'a, C, D, R> -where - C: AsRef, - D: AsRef, - R: CommandRunner, -{ - fn get_pairs(&self) -> Vec<(String, String)> { +impl<'a, C: AsRef, D: AsRef, R: CommandRunner> WordpressTranslation<'a, C, D, R> { + fn get_pairs(&self) -> Vec<(String, PathBuf)> { let version_x = self .version .trim_end_matches(|c: char| c.is_digit(10)) .to_owned() + "x"; - let locale: &str = self.locale.as_ref(); + let locale = self.locale.as_ref(); let path_locale = if locale == "de_DE" { "de".to_owned() } else { @@ -71,7 +58,7 @@ where for format in ["po", "mo"].iter() { res.push(( format!("https://translate.wordpress.org/projects/wp/{}/{}{}/default/export-translations?format={}", version_x, in_slug, path_locale, format), - format!("{}/{}{}.{}", self.path.as_ref(), out_slug, self.locale.as_ref(), format) + [self.path.as_ref(), format!("{}{}.{}", out_slug, self.locale.as_ref(), format).as_ref()].iter().collect() )) } } @@ -79,11 +66,8 @@ where } } -impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R> -where - C: AsRef, - D: AsRef, - R: CommandRunner, +impl<'a, C: AsRef, D: AsRef, R: CommandRunner> Symbol + for WordpressTranslation<'a, C, D, R> { fn target_reached(&self) -> Result> { let mut newest = String::new(); @@ -113,7 +97,7 @@ where } let upstream = self.command_runner.get_output( "curl", - &[&format!( + args![format!( "https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}", self.version, self.locale.as_ref() @@ -131,13 +115,13 @@ where for (source, target) in self.get_pairs() { self .command_runner - .run_successfully("curl", &["--compressed", "-o", &target, &source])?; + .run_successfully("curl", args!["--compressed", "-o", target, source,])?; } Ok(()) } fn get_prerequisites(&self) -> Vec { - vec![Resource::new("dir", self.path.as_ref())] + vec![Resource::new("dir", self.path.as_ref().to_str().unwrap())] } fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box { @@ -152,13 +136,10 @@ where } } -impl<'a, C, D, R> fmt::Display for WordpressTranslation<'a, C, D, R> -where - C: AsRef, - D: AsRef, - R: CommandRunner, +impl<'a, C: AsRef, D: AsRef, R: CommandRunner> fmt::Display + for WordpressTranslation<'a, C, D, R> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "WordpressTranslation {}", self.path.as_ref()) + write!(f, "WordpressTranslation {}", self.path.as_ref().display()) } }