commit
92cafbdc8e
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -370,7 +370,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "espanso"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "espanso"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
description = "Cross-platform Text Expander written in Rust"
|
||||
|
@ -30,13 +30,11 @@ git2 = {version = "0.10.1", features = ["https"]}
|
|||
tempfile = "3.1.0"
|
||||
dialoguer = "0.4.0"
|
||||
rand = "0.7.2"
|
||||
zip = "0.5.3"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.62"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
zip = "0.5.3"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1.31"
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path) {
|
|||
|
||||
Rid[1].usUsagePage = 0x01;
|
||||
Rid[1].usUsage = 0x02;
|
||||
Rid[1].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK; // adds HID mouse and also ignores legacy mouse messages
|
||||
Rid[1].dwFlags = RIDEV_INPUTSINK; // adds HID mouse and also ignores legacy mouse messages
|
||||
Rid[1].hwndTarget = window;
|
||||
|
||||
if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE) { // Something went wrong, error.
|
||||
|
|
|
@ -82,10 +82,12 @@ def build_windows(package_info):
|
|||
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
|
||||
print("Found Redists: ", msvc_dirs)
|
||||
|
||||
msvc_dir = msvc_dirs[0]
|
||||
print("Using: ",msvc_dir)
|
||||
if len(msvc_dir) == 0:
|
||||
if len(msvc_dirs) == 0:
|
||||
raise Exception("Cannot find redistributable dlls")
|
||||
|
||||
msvc_dir = msvc_dirs[-1] # Take the most recent version of the toolchain
|
||||
print("Using: ",msvc_dir)
|
||||
|
||||
dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
|
||||
|
||||
print("Found DLLs:")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: espanso
|
||||
version: 0.5.2
|
||||
version: 0.5.3
|
||||
summary: A Cross-platform Text Expander written in Rust
|
||||
description: |
|
||||
espanso is a Cross-platform, Text Expander written in Rust.
|
||||
|
|
|
@ -47,7 +47,7 @@ fn default_filter_title() -> String{ "".to_owned() }
|
|||
fn default_filter_class() -> String{ "".to_owned() }
|
||||
fn default_filter_exec() -> String{ "".to_owned() }
|
||||
fn default_log_level() -> i32 { 0 }
|
||||
fn default_conflict_check() -> bool{ true }
|
||||
fn default_conflict_check() -> bool{ false }
|
||||
fn default_ipc_server_port() -> i32 { 34982 }
|
||||
fn default_use_system_agent() -> bool { true }
|
||||
fn default_config_caching_interval() -> i32 { 800 }
|
||||
|
|
68
src/main.rs
68
src/main.rs
|
@ -43,8 +43,10 @@ use crate::ui::UIManager;
|
|||
use crate::protocol::*;
|
||||
use std::io::{BufReader, BufRead};
|
||||
use crate::package::default::DefaultPackageManager;
|
||||
use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult};
|
||||
use crate::package::{PackageManager, InstallResult, UpdateResult, RemoveResult, PackageResolver};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use crate::package::git::GitPackageResolver;
|
||||
use crate::package::zip::ZipPackageResolver;
|
||||
|
||||
mod ui;
|
||||
mod edit;
|
||||
|
@ -71,6 +73,12 @@ const LOG_FILE: &str = "espanso.log";
|
|||
fn main() {
|
||||
let install_subcommand = SubCommand::with_name("install")
|
||||
.about("Install a package. Equivalent to 'espanso package install'")
|
||||
.arg(Arg::with_name("no-git")
|
||||
.short("g")
|
||||
.long("no-git")
|
||||
.required(false)
|
||||
.takes_value(false)
|
||||
.help("Install packages avoiding the GIT package provider. Try this flag if the default mode is not working."))
|
||||
.arg(Arg::with_name("package_name")
|
||||
.help("Package name"));
|
||||
|
||||
|
@ -426,6 +434,7 @@ fn start_daemon(config_set: ConfigSet) {
|
|||
#[cfg(target_os = "linux")]
|
||||
fn start_daemon(config_set: ConfigSet) {
|
||||
use std::process::{Command, Stdio};
|
||||
use crate::sysdaemon::{verify, VerifyResult};
|
||||
|
||||
// Check if Systemd is available in the system
|
||||
let status = Command::new("systemctl")
|
||||
|
@ -444,25 +453,33 @@ fn start_daemon(config_set: ConfigSet) {
|
|||
|
||||
if config_set.default.use_system_agent && !force_unmanaged {
|
||||
// Make sure espanso is currently registered in systemd
|
||||
let res = Command::new("systemctl")
|
||||
.args(&["--user", "is-enabled", "espanso.service"])
|
||||
.output();
|
||||
if !res.unwrap().status.success() {
|
||||
use dialoguer::Confirmation;
|
||||
if Confirmation::new()
|
||||
.with_text("espanso must be registered to systemd (user level) first. Do you want to proceed?")
|
||||
.default(true)
|
||||
.show_default(true)
|
||||
.interact().expect("Unable to read user answer") {
|
||||
|
||||
let res = verify();
|
||||
match res {
|
||||
VerifyResult::EnabledAndValid => {
|
||||
// Do nothing, everything is ok!
|
||||
},
|
||||
VerifyResult::EnabledButInvalidPath => {
|
||||
eprintln!("Updating espanso service file with new path...");
|
||||
unregister_main(config_set.clone());
|
||||
register_main(config_set);
|
||||
}else{
|
||||
eprintln!("Please register espanso to systemd with this command:");
|
||||
eprintln!(" espanso register");
|
||||
// TODO: enable flag to use non-managed daemon mode
|
||||
},
|
||||
VerifyResult::NotEnabled => {
|
||||
use dialoguer::Confirmation;
|
||||
if Confirmation::new()
|
||||
.with_text("espanso must be registered to systemd (user level) first. Do you want to proceed?")
|
||||
.default(true)
|
||||
.show_default(true)
|
||||
.interact().expect("Unable to read user answer") {
|
||||
|
||||
std::process::exit(4);
|
||||
}
|
||||
register_main(config_set);
|
||||
}else{
|
||||
eprintln!("Please register espanso to systemd with this command:");
|
||||
eprintln!(" espanso register");
|
||||
// TODO: enable flag to use non-managed daemon mode
|
||||
|
||||
std::process::exit(4);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Start the espanso service
|
||||
|
@ -742,7 +759,14 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
|
|||
exit(1);
|
||||
});
|
||||
|
||||
let mut package_manager = DefaultPackageManager::new_default();
|
||||
let package_resolver: Box<dyn PackageResolver> = if matches.is_present("no-git") {
|
||||
println!("Using alternative package provider");
|
||||
Box::new(ZipPackageResolver::new())
|
||||
}else{
|
||||
Box::new(GitPackageResolver::new())
|
||||
};
|
||||
|
||||
let mut package_manager = DefaultPackageManager::new_default(Some(package_resolver));
|
||||
|
||||
if package_manager.is_index_outdated() {
|
||||
println!("Updating package index...");
|
||||
|
@ -808,7 +832,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
|
|||
exit(1);
|
||||
});
|
||||
|
||||
let package_manager = DefaultPackageManager::new_default();
|
||||
let package_manager = DefaultPackageManager::new_default(None);
|
||||
|
||||
let res = package_manager.remove_package(package_name);
|
||||
|
||||
|
@ -833,7 +857,7 @@ fn remove_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
|
|||
}
|
||||
|
||||
fn update_index_main(_config_set: ConfigSet) {
|
||||
let mut package_manager = DefaultPackageManager::new_default();
|
||||
let mut package_manager = DefaultPackageManager::new_default(None);
|
||||
|
||||
let res = package_manager.update_index(true);
|
||||
|
||||
|
@ -856,7 +880,7 @@ fn update_index_main(_config_set: ConfigSet) {
|
|||
}
|
||||
|
||||
fn list_package_main(_config_set: ConfigSet, matches: &ArgMatches) {
|
||||
let package_manager = DefaultPackageManager::new_default();
|
||||
let package_manager = DefaultPackageManager::new_default(None);
|
||||
|
||||
let list = package_manager.list_local_packages();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
use std::path::{PathBuf, Path};
|
||||
use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult};
|
||||
use crate::package::{PackageIndex, UpdateResult, Package, InstallResult, RemoveResult, PackageResolver};
|
||||
use std::error::Error;
|
||||
use std::fs::{File, create_dir};
|
||||
use std::io::{BufReader, BufRead};
|
||||
|
@ -31,6 +31,7 @@ use git2::Repository;
|
|||
use regex::Regex;
|
||||
use crate::package::RemoveResult::Removed;
|
||||
use std::collections::HashMap;
|
||||
use super::git::GitPackageResolver;
|
||||
|
||||
const DEFAULT_PACKAGE_INDEX_FILE : &str = "package_index.json";
|
||||
|
||||
|
@ -38,24 +39,28 @@ pub struct DefaultPackageManager {
|
|||
package_dir: PathBuf,
|
||||
data_dir: PathBuf,
|
||||
|
||||
package_resolver: Option<Box<dyn PackageResolver>>,
|
||||
|
||||
local_index: Option<PackageIndex>,
|
||||
}
|
||||
|
||||
impl DefaultPackageManager {
|
||||
pub fn new(package_dir: PathBuf, data_dir: PathBuf) -> DefaultPackageManager {
|
||||
pub fn new(package_dir: PathBuf, data_dir: PathBuf, package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
|
||||
let local_index = Self::load_local_index(&data_dir);
|
||||
|
||||
DefaultPackageManager{
|
||||
package_dir,
|
||||
data_dir,
|
||||
package_resolver,
|
||||
local_index
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_default() -> DefaultPackageManager {
|
||||
pub fn new_default(package_resolver: Option<Box<dyn PackageResolver>>) -> DefaultPackageManager {
|
||||
DefaultPackageManager::new(
|
||||
crate::context::get_package_dir(),
|
||||
crate::context::get_data_dir()
|
||||
crate::context::get_data_dir(),
|
||||
package_resolver,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -89,12 +94,6 @@ impl DefaultPackageManager {
|
|||
Ok(index)
|
||||
}
|
||||
|
||||
fn clone_repo_to_temp(repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let _repo = Repository::clone(repo_url, temp_dir.path())?;
|
||||
Ok(temp_dir)
|
||||
}
|
||||
|
||||
fn parse_package_from_readme(readme_path: &Path) -> Option<Package> {
|
||||
lazy_static! {
|
||||
static ref FIELD_REGEX: Regex = Regex::new(r###"^\s*(.*?)\s*:\s*"?(.*?)"?$"###).unwrap();
|
||||
|
@ -248,7 +247,7 @@ impl super::PackageManager for DefaultPackageManager {
|
|||
return Ok(AlreadyInstalled);
|
||||
}
|
||||
|
||||
let temp_dir = Self::clone_repo_to_temp(repo_url)?;
|
||||
let temp_dir = self.package_resolver.as_ref().unwrap().clone_repo_to_temp(repo_url)?;
|
||||
|
||||
let temp_package_dir = temp_dir.path().join(name);
|
||||
if !temp_package_dir.exists() {
|
||||
|
@ -337,7 +336,8 @@ mod tests {
|
|||
|
||||
let package_manager = DefaultPackageManager::new(
|
||||
package_dir.path().clone().to_path_buf(),
|
||||
data_dir.path().clone().to_path_buf()
|
||||
data_dir.path().clone().to_path_buf(),
|
||||
Some(Box::new(GitPackageResolver::new())),
|
||||
);
|
||||
|
||||
TempPackageManager {
|
||||
|
@ -465,12 +465,6 @@ mod tests {
|
|||
assert_eq!(temp.package_manager.install_package("italian-accents").unwrap(), AlreadyInstalled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clone_temp_repository() {
|
||||
let cloned_dir = DefaultPackageManager::clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
|
||||
assert!(cloned_dir.path().join("LICENSE").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_install_package() {
|
||||
let mut temp = create_temp_package_manager(|_, data_dir| {
|
||||
|
|
33
src/package/git.rs
Normal file
33
src/package/git.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use tempfile::TempDir;
|
||||
use std::error::Error;
|
||||
use git2::Repository;
|
||||
use super::PackageResolver;
|
||||
|
||||
pub struct GitPackageResolver;
|
||||
|
||||
impl GitPackageResolver {
|
||||
pub fn new() -> GitPackageResolver {
|
||||
return GitPackageResolver{};
|
||||
}
|
||||
}
|
||||
|
||||
impl super::PackageResolver for GitPackageResolver {
|
||||
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let _repo = Repository::clone(repo_url, temp_dir.path())?;
|
||||
Ok(temp_dir)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::{TempDir, NamedTempFile};
|
||||
|
||||
#[test]
|
||||
fn test_clone_temp_repository() {
|
||||
let resolver = GitPackageResolver::new();
|
||||
let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
|
||||
assert!(cloned_dir.path().join("LICENSE").exists());
|
||||
}
|
||||
}
|
|
@ -17,9 +17,13 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub(crate) mod git;
|
||||
pub(crate) mod zip;
|
||||
pub(crate) mod default;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::error::Error;
|
||||
use tempfile::TempDir;
|
||||
|
||||
pub trait PackageManager {
|
||||
fn is_index_outdated(&self) -> bool;
|
||||
|
@ -35,6 +39,10 @@ pub trait PackageManager {
|
|||
fn list_local_packages(&self) -> Vec<Package>;
|
||||
}
|
||||
|
||||
pub trait PackageResolver {
|
||||
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Package {
|
||||
pub name: String,
|
||||
|
|
88
src/package/zip.rs
Normal file
88
src/package/zip.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use tempfile::TempDir;
|
||||
use std::error::Error;
|
||||
use super::PackageResolver;
|
||||
use std::io::{Cursor, copy, Read};
|
||||
use std::{fs, io};
|
||||
use std::fs::File;
|
||||
use log::debug;
|
||||
|
||||
pub struct ZipPackageResolver;
|
||||
|
||||
impl ZipPackageResolver {
|
||||
pub fn new() -> ZipPackageResolver {
|
||||
return ZipPackageResolver{};
|
||||
}
|
||||
}
|
||||
|
||||
impl super::PackageResolver for ZipPackageResolver {
|
||||
fn clone_repo_to_temp(&self, repo_url: &str) -> Result<TempDir, Box<dyn Error>> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
|
||||
let zip_url = repo_url.to_owned() + "/archive/master.zip";
|
||||
|
||||
// Download the archive from GitHub
|
||||
let mut response = reqwest::get(&zip_url)?;
|
||||
|
||||
// Extract zip file
|
||||
let mut buffer = Vec::new();
|
||||
copy(&mut response, &mut buffer)?;
|
||||
|
||||
let reader = Cursor::new(buffer);
|
||||
|
||||
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
||||
|
||||
// Find the root folder name
|
||||
let mut root_folder = {
|
||||
let mut root_folder = archive.by_index(0).unwrap();
|
||||
let root_folder = root_folder.sanitized_name();
|
||||
root_folder.to_str().unwrap().to_owned()
|
||||
};
|
||||
root_folder.push(std::path::MAIN_SEPARATOR);
|
||||
|
||||
for i in 1..archive.len() {
|
||||
let mut file = archive.by_index(i).unwrap();
|
||||
|
||||
let current_path = file.sanitized_name();
|
||||
let current_filename = current_path.to_str().unwrap();
|
||||
let trimmed_filename = current_filename.trim_start_matches(&root_folder);
|
||||
|
||||
let outpath = temp_dir.path().join(trimmed_filename);
|
||||
|
||||
{
|
||||
let comment = file.comment();
|
||||
if !comment.is_empty() {
|
||||
debug!("File {} comment: {}", i, comment);
|
||||
}
|
||||
}
|
||||
|
||||
if (&*file.name()).ends_with('/') {
|
||||
debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
|
||||
fs::create_dir_all(&outpath).unwrap();
|
||||
} else {
|
||||
debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
|
||||
if let Some(p) = outpath.parent() {
|
||||
if !p.exists() {
|
||||
fs::create_dir_all(&p).unwrap();
|
||||
}
|
||||
}
|
||||
let mut outfile = fs::File::create(&outpath).unwrap();
|
||||
io::copy(&mut file, &mut outfile).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(temp_dir)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::{TempDir, NamedTempFile};
|
||||
|
||||
#[test]
|
||||
fn test_clone_temp_repository() {
|
||||
let resolver = ZipPackageResolver::new();
|
||||
let cloned_dir = resolver.clone_repo_to_temp("https://github.com/federico-terzi/espanso-hub-core").unwrap();
|
||||
assert!(cloned_dir.path().join("LICENSE").exists());
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
// This functions are used to register/unregister espanso from the system daemon manager.
|
||||
|
||||
use crate::config::ConfigSet;
|
||||
use crate::sysdaemon::VerifyResult::{EnabledAndValid, NotEnabled, EnabledButInvalidPath};
|
||||
|
||||
// INSTALLATION
|
||||
|
||||
|
@ -181,6 +182,54 @@ pub fn register(config_set: ConfigSet) {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum VerifyResult {
|
||||
EnabledAndValid,
|
||||
EnabledButInvalidPath,
|
||||
NotEnabled,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn verify() -> VerifyResult {
|
||||
use regex::Regex;
|
||||
use std::process::{Command, ExitStatus};
|
||||
|
||||
// Check if espanso service is already registered
|
||||
let res = Command::new("systemctl")
|
||||
.args(&["--user", "is-enabled", "espanso"])
|
||||
.output();
|
||||
if let Ok(res) = res {
|
||||
let output = String::from_utf8_lossy(res.stdout.as_slice());
|
||||
let output = output.trim();
|
||||
if !res.status.success() || output != "enabled" {
|
||||
return NotEnabled
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ExecPathRegex: Regex = Regex::new("ExecStart=(?P<path>.*?)\\s").unwrap();
|
||||
}
|
||||
|
||||
// Check if the currently registered path is valid
|
||||
let res = Command::new("systemctl")
|
||||
.args(&["--user", "cat", "espanso"])
|
||||
.output();
|
||||
if let Ok(res) = res {
|
||||
let output = String::from_utf8_lossy(res.stdout.as_slice());
|
||||
let output = output.trim();
|
||||
if res.status.success() {
|
||||
let caps = ExecPathRegex.captures(output).unwrap();
|
||||
let path = caps.get(1).map_or("", |m| m.as_str());
|
||||
let espanso_path = std::env::current_exe().expect("Could not get espanso executable path");
|
||||
|
||||
if espanso_path.to_string_lossy() != path {
|
||||
return EnabledButInvalidPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnabledAndValid
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn unregister(config_set: ConfigSet) {
|
||||
use std::process::{Command, ExitStatus};
|
||||
|
|
Loading…
Reference in New Issue
Block a user