commit
595edab828
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -329,7 +329,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "espanso"
|
name = "espanso"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
description = "Cross-platform Text Expander written in Rust"
|
description = "Cross-platform Text Expander written in Rust"
|
||||||
|
|
113
README.md
113
README.md
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
![example](images/example.gif)
|
![example](images/example.gif)
|
||||||
|
|
||||||
|
Visit the [espanso website](https://espanso.org).
|
||||||
|
|
||||||
#### What is a Text Expander?
|
#### What is a Text Expander?
|
||||||
|
|
||||||
A *text expander* is a program that detects when you type
|
A *text expander* is a program that detects when you type
|
||||||
|
@ -30,111 +32,13 @@ ___
|
||||||
* **Custom scripts** support
|
* **Custom scripts** support
|
||||||
* **Shell commands** support
|
* **Shell commands** support
|
||||||
* **App-specific** configurations
|
* **App-specific** configurations
|
||||||
|
* Expandable with **packages**
|
||||||
|
* Built-in **package manager** for [espanso hub](https://hub.espanso.org/)
|
||||||
* File based configuration
|
* File based configuration
|
||||||
|
|
||||||
## Table of contents
|
## Get Started
|
||||||
|
|
||||||
- [Installation](#installation)
|
Visit the [official documentation](https://espanso.org/docs/).
|
||||||
- [Windows](#install-windows)
|
|
||||||
- [Linux](#install-linux)
|
|
||||||
- [macOS](#install-macos)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [FAQ](#faq)
|
|
||||||
- [Donations](#donations)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### <a name="install-windows"></a>Windows
|
|
||||||
|
|
||||||
The installation on Windows is pretty straightforward, navigate to the
|
|
||||||
[Release](https://github.com/federico-terzi/espanso/releases) page and
|
|
||||||
download the latest installer ( usually named like
|
|
||||||
`espanso-win-0.1.0.exe` ).
|
|
||||||
|
|
||||||
Because espanso is not digitally signed, you may experience a warning from
|
|
||||||
Windows Smartscreen. In this case, just click on "More info" (1) and then
|
|
||||||
on "Run anyway" (2), as shown in the picture:
|
|
||||||
|
|
||||||
![Windows Smartscreen](images/windows-smartscreen.png)
|
|
||||||
|
|
||||||
If you completed the installation procedure, you should have espanso running.
|
|
||||||
A good way to find out is by going on any text field and typing `:espanso`.
|
|
||||||
You should see "Hi there!" appear.
|
|
||||||
|
|
||||||
### <a name="install-linux"></a>Linux
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
### <a name="install-macos"></a>MacOS
|
|
||||||
|
|
||||||
The easiest way to install espanso on macOS using the [Homebrew](https://brew.sh/)
|
|
||||||
package manager, but you can also do it manually.
|
|
||||||
|
|
||||||
#### Using Homebrew
|
|
||||||
|
|
||||||
The first thing to do is to add the official espanso *tap* to Homebrew with
|
|
||||||
the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
brew tap federico-terzi/espanso
|
|
||||||
```
|
|
||||||
|
|
||||||
Then you can install espanso with:
|
|
||||||
|
|
||||||
```
|
|
||||||
brew install espanso
|
|
||||||
```
|
|
||||||
|
|
||||||
To make sure that espanso was correctly installed, you can open a terminal and type:
|
|
||||||
|
|
||||||
```
|
|
||||||
espanso --version
|
|
||||||
```
|
|
||||||
|
|
||||||
At this point, you have to [Enable Accessibility](#enabling-accessibility) to use espanso.
|
|
||||||
|
|
||||||
#### Enabling Accessibility
|
|
||||||
|
|
||||||
Because espanso uses the macOS [Accessibility API](https://developer.apple.com/library/archive/documentation/Accessibility/Conceptual/AccessibilityMacOSX/)
|
|
||||||
to work, you need to authorize it using the following procedure:
|
|
||||||
|
|
||||||
Open a terminal and type the command:
|
|
||||||
|
|
||||||
```
|
|
||||||
espanso install
|
|
||||||
```
|
|
||||||
|
|
||||||
A dialog should show up, click on "Open System Preferences", as shown here:
|
|
||||||
|
|
||||||
![Accessibility Prompt](images/accessibility-prompt.png)
|
|
||||||
|
|
||||||
Then, in the "Privacy" panel click on the Lock icon (1) to enable edits and
|
|
||||||
then check "espanso" (2), as shown in the picture:
|
|
||||||
|
|
||||||
![Accessibility Settings](images/accessibility-macos-enable.png)
|
|
||||||
|
|
||||||
Now open the terminal again and type:
|
|
||||||
|
|
||||||
```
|
|
||||||
espanso install
|
|
||||||
```
|
|
||||||
|
|
||||||
If everything goes well, you should see the espanso icon appear in the status bar:
|
|
||||||
|
|
||||||
![macOS status bar icon](images/espanso-icon-macos-statusbar.png)
|
|
||||||
|
|
||||||
If you now type `:espanso` in any text field, you should see "Hi there!" appear!
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
#### How does espanso work?
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
|
||||||
|
@ -144,6 +48,11 @@ please consider making a small donation, it really helps :)
|
||||||
|
|
||||||
[![Donate with PayPal](images/donate.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FHNLR5DRS267E&source=url)
|
[![Donate with PayPal](images/donate.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FHNLR5DRS267E&source=url)
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
* Special thanks to the [ModifyPath](https://www.legroom.net/software/modpath)
|
||||||
|
script, used by espanso to improve the Windows installer.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
espanso was created by [Federico Terzi](http://federicoterzi.com)
|
espanso was created by [Federico Terzi](http://federicoterzi.com)
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn check_dependencies() -> bool {
|
||||||
let status = Command::new("notify-send")
|
let status = Command::new("notify-send")
|
||||||
.arg("-v")
|
.arg("-v")
|
||||||
.output();
|
.output();
|
||||||
if let Err(_) = status {
|
if status.is_err() {
|
||||||
println!("Error: 'notify-send' command is needed for espanso to work correctly, please install it.");
|
println!("Error: 'notify-send' command is needed for espanso to work correctly, please install it.");
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ pub fn check_dependencies() -> bool {
|
||||||
let status = Command::new("xclip")
|
let status = Command::new("xclip")
|
||||||
.arg("-version")
|
.arg("-version")
|
||||||
.output();
|
.output();
|
||||||
if let Err(_) = status {
|
if status.is_err() {
|
||||||
println!("Error: 'xclip' command is needed for espanso to work correctly, please install it.");
|
println!("Error: 'xclip' command is needed for espanso to work correctly, please install it.");
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
pub(crate) mod runtime;
|
pub(crate) mod runtime;
|
||||||
|
|
||||||
// TODO: add documentation link
|
|
||||||
const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
|
const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
|
||||||
|
|
||||||
const DEFAULT_CONFIG_FILE_NAME : &str = "default.yml";
|
const DEFAULT_CONFIG_FILE_NAME : &str = "default.yml";
|
||||||
|
@ -162,7 +161,7 @@ impl Configs {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
let res = file.read_to_string(&mut contents);
|
let res = file.read_to_string(&mut contents);
|
||||||
|
|
||||||
if let Err(_) = res {
|
if res.is_err() {
|
||||||
return Err(ConfigLoadError::UnableToReadFile)
|
return Err(ConfigLoadError::UnableToReadFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +186,7 @@ impl Configs {
|
||||||
});
|
});
|
||||||
let parent_matches : Vec<Match> = self.matches.iter().filter(|&m| {
|
let parent_matches : Vec<Match> = self.matches.iter().filter(|&m| {
|
||||||
!trigger_set.contains(&m.trigger)
|
!trigger_set.contains(&m.trigger)
|
||||||
}).map(|m| m.clone()).collect();
|
}).cloned().collect();
|
||||||
|
|
||||||
merged_matches.extend(parent_matches);
|
merged_matches.extend(parent_matches);
|
||||||
self.matches = merged_matches;
|
self.matches = merged_matches;
|
||||||
|
@ -200,7 +199,7 @@ impl Configs {
|
||||||
});
|
});
|
||||||
let default_matches : Vec<Match> = default.matches.iter().filter(|&m| {
|
let default_matches : Vec<Match> = default.matches.iter().filter(|&m| {
|
||||||
!trigger_set.contains(&m.trigger)
|
!trigger_set.contains(&m.trigger)
|
||||||
}).map(|m| m.clone()).collect();
|
}).cloned().collect();
|
||||||
|
|
||||||
self.matches.extend(default_matches);
|
self.matches.extend(default_matches);
|
||||||
}
|
}
|
||||||
|
@ -326,7 +325,7 @@ impl ConfigSet {
|
||||||
// Create the espanso dir if id doesn't exist
|
// Create the espanso dir if id doesn't exist
|
||||||
let res = create_dir_all(espanso_dir.as_path());
|
let res = create_dir_all(espanso_dir.as_path());
|
||||||
|
|
||||||
if let Ok(_) = res {
|
if res.is_ok() {
|
||||||
let default_file = espanso_dir.join(DEFAULT_CONFIG_FILE_NAME);
|
let default_file = espanso_dir.join(DEFAULT_CONFIG_FILE_NAME);
|
||||||
|
|
||||||
// If config file does not exist, create one from template
|
// If config file does not exist, create one from template
|
||||||
|
@ -358,13 +357,12 @@ impl ConfigSet {
|
||||||
return ConfigSet::load(espanso_dir.as_path())
|
return ConfigSet::load(espanso_dir.as_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(ConfigLoadError::UnableToCreateDefaultConfig)
|
Err(ConfigLoadError::UnableToCreateDefaultConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_config_dir() -> PathBuf {
|
pub fn get_default_config_dir() -> PathBuf {
|
||||||
let home_dir = dirs::home_dir().expect("Unable to get home directory");
|
let home_dir = dirs::home_dir().expect("Unable to get home directory");
|
||||||
let espanso_dir = home_dir.join(".espanso");
|
home_dir.join(".espanso")
|
||||||
espanso_dir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_packages_dir() -> PathBuf {
|
pub fn get_default_packages_dir() -> PathBuf {
|
||||||
|
|
|
@ -27,7 +27,7 @@ use std::fs;
|
||||||
use log::{info, error};
|
use log::{info, error};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
const STATUS_ICON_BINARY : &'static [u8] = include_bytes!("../res/mac/icon.png");
|
const STATUS_ICON_BINARY : &[u8] = include_bytes!("../res/mac/icon.png");
|
||||||
|
|
||||||
pub struct MacContext {
|
pub struct MacContext {
|
||||||
pub send_channel: Sender<Event>
|
pub send_channel: Sender<Event>
|
||||||
|
@ -42,7 +42,7 @@ impl MacContext {
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
error!("Accessibility must be enabled to make espanso work on MacOS.");
|
error!("Accessibility must be enabled to make espanso work on MacOS.");
|
||||||
error!("Please allow espanso in the Security & Privacy panel, then restart espanso.");
|
error!("Please allow espanso in the Security & Privacy panel, then restart espanso.");
|
||||||
error!("For more information: "); // TODO: add documentation link
|
error!("For more information: https://espanso.org/install/mac/");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ use std::{fs};
|
||||||
use widestring::U16CString;
|
use widestring::U16CString;
|
||||||
use log::{info};
|
use log::{info};
|
||||||
|
|
||||||
const BMP_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.bmp");
|
const BMP_BINARY : &[u8] = include_bytes!("../res/win/espanso.bmp");
|
||||||
const ICO_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.ico");
|
const ICO_BINARY : &[u8] = include_bytes!("../res/win/espanso.ico");
|
||||||
|
|
||||||
pub struct WindowsContext {
|
pub struct WindowsContext {
|
||||||
send_channel: Sender<Event>,
|
send_channel: Sender<Event>,
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl <'a> EventManager for DefaultEventManager<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => panic!("Broken event channel"),
|
Err(e) => panic!("Broken event channel {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -63,7 +63,7 @@ mod clipboard;
|
||||||
mod extension;
|
mod extension;
|
||||||
mod sysdaemon;
|
mod sysdaemon;
|
||||||
|
|
||||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
const LOG_FILE: &str = "espanso.log";
|
const LOG_FILE: &str = "espanso.log";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -168,52 +168,52 @@ fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("dump") {
|
if matches.subcommand_matches("dump").is_some() {
|
||||||
println!("{:#?}", config_set);
|
println!("{:#?}", config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("detect") {
|
if matches.subcommand_matches("detect").is_some() {
|
||||||
detect_main();
|
detect_main();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("daemon") {
|
if matches.subcommand_matches("daemon").is_some() {
|
||||||
daemon_main(config_set);
|
daemon_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("register") {
|
if matches.subcommand_matches("register").is_some() {
|
||||||
register_main(config_set);
|
register_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("unregister") {
|
if matches.subcommand_matches("unregister").is_some() {
|
||||||
unregister_main(config_set);
|
unregister_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("log") {
|
if matches.subcommand_matches("log").is_some() {
|
||||||
log_main();
|
log_main();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("start") {
|
if matches.subcommand_matches("start").is_some() {
|
||||||
start_main(config_set);
|
start_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("status") {
|
if matches.subcommand_matches("status").is_some() {
|
||||||
status_main();
|
status_main();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("stop") {
|
if matches.subcommand_matches("stop").is_some() {
|
||||||
stop_main(config_set);
|
stop_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("restart") {
|
if matches.subcommand_matches("restart").is_some() {
|
||||||
restart_main(config_set);
|
restart_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ fn main() {
|
||||||
list_package_main(config_set, matches);
|
list_package_main(config_set, matches);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(_) = matches.subcommand_matches("refresh") {
|
if matches.subcommand_matches("refresh").is_some() {
|
||||||
update_index_main(config_set);
|
update_index_main(config_set);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -244,6 +244,7 @@ fn main() {
|
||||||
|
|
||||||
// Defaults help print
|
// Defaults help print
|
||||||
clap_instance.print_long_help().expect("Unable to print help");
|
clap_instance.print_long_help().expect("Unable to print help");
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Daemon subcommand, start the event loop and spawn a background thread worker
|
/// Daemon subcommand, start the event loop and spawn a background thread worker
|
||||||
|
@ -524,22 +525,22 @@ fn detect_main() {
|
||||||
|
|
||||||
/// Send the given command to the espanso daemon
|
/// Send the given command to the espanso daemon
|
||||||
fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
|
fn cmd_main(config_set: ConfigSet, matches: &ArgMatches) {
|
||||||
let command = if let Some(_) = matches.subcommand_matches("exit") {
|
let command = if matches.subcommand_matches("exit").is_some() {
|
||||||
Some(IPCCommand {
|
Some(IPCCommand {
|
||||||
id: String::from("exit"),
|
id: String::from("exit"),
|
||||||
payload: String::from(""),
|
payload: String::from(""),
|
||||||
})
|
})
|
||||||
}else if let Some(_) = matches.subcommand_matches("toggle") {
|
}else if matches.subcommand_matches("toggle").is_some() {
|
||||||
Some(IPCCommand {
|
Some(IPCCommand {
|
||||||
id: String::from("toggle"),
|
id: String::from("toggle"),
|
||||||
payload: String::from(""),
|
payload: String::from(""),
|
||||||
})
|
})
|
||||||
}else if let Some(_) = matches.subcommand_matches("enable") {
|
}else if matches.subcommand_matches("enable").is_some() {
|
||||||
Some(IPCCommand {
|
Some(IPCCommand {
|
||||||
id: String::from("enable"),
|
id: String::from("enable"),
|
||||||
payload: String::from(""),
|
payload: String::from(""),
|
||||||
})
|
})
|
||||||
}else if let Some(_) = matches.subcommand_matches("disable") {
|
}else if matches.subcommand_matches("disable").is_some() {
|
||||||
Some(IPCCommand {
|
Some(IPCCommand {
|
||||||
id: String::from("disable"),
|
id: String::from("disable"),
|
||||||
payload: String::from(""),
|
payload: String::from(""),
|
||||||
|
@ -747,7 +748,7 @@ fn acquire_lock() -> Option<File> {
|
||||||
|
|
||||||
let res = file.try_lock_exclusive();
|
let res = file.try_lock_exclusive();
|
||||||
|
|
||||||
if let Ok(_) = res {
|
if res.is_ok() {
|
||||||
return Some(file)
|
return Some(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMa
|
||||||
if m == config.toggle_key {
|
if m == config.toggle_key {
|
||||||
let mut toggle_press_time = self.toggle_press_time.borrow_mut();
|
let mut toggle_press_time = self.toggle_press_time.borrow_mut();
|
||||||
if let Ok(elapsed) = toggle_press_time.elapsed() {
|
if let Ok(elapsed) = toggle_press_time.elapsed() {
|
||||||
if elapsed.as_millis() < config.toggle_interval as u128 {
|
if elapsed.as_millis() < u128::from(config.toggle_interval) {
|
||||||
self.toggle();
|
self.toggle();
|
||||||
|
|
||||||
let is_enabled = self.is_enabled.borrow();
|
let is_enabled = self.is_enabled.borrow();
|
||||||
|
|
|
@ -117,16 +117,14 @@ impl DefaultPackageManager {
|
||||||
}else{
|
}else{
|
||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
}else{
|
}else if started {
|
||||||
if started {
|
let caps = FIELD_REGEX.captures(&line);
|
||||||
let caps = FIELD_REGEX.captures(&line);
|
if let Some(caps) = caps {
|
||||||
if let Some(caps) = caps {
|
let property = caps.get(1);
|
||||||
let property = caps.get(1);
|
let value = caps.get(2);
|
||||||
let value = caps.get(2);
|
if property.is_some() && value.is_some() {
|
||||||
if property.is_some() && value.is_some() {
|
fields.insert(property.unwrap().as_str().to_owned(),
|
||||||
fields.insert(property.unwrap().as_str().to_owned(),
|
value.unwrap().as_str().to_owned());
|
||||||
value.unwrap().as_str().to_owned());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +159,7 @@ impl DefaultPackageManager {
|
||||||
return local_index.last_update
|
return local_index.last_update
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_local_packages_names(&self) -> Vec<String> {
|
fn list_local_packages_names(&self) -> Vec<String> {
|
||||||
|
@ -260,14 +258,14 @@ impl super::PackageManager for DefaultPackageManager {
|
||||||
let readme_path = temp_package_dir.join("README.md");
|
let readme_path = temp_package_dir.join("README.md");
|
||||||
|
|
||||||
let package = Self::parse_package_from_readme(&readme_path);
|
let package = Self::parse_package_from_readme(&readme_path);
|
||||||
if !package.is_some() {
|
if package.is_none() {
|
||||||
return Ok(InstallResult::UnableToParsePackageInfo); // TODO: test
|
return Ok(InstallResult::UnableToParsePackageInfo);
|
||||||
}
|
}
|
||||||
let package = package.unwrap();
|
let package = package.unwrap();
|
||||||
|
|
||||||
let source_dir = temp_package_dir.join(package.version);
|
let source_dir = temp_package_dir.join(package.version);
|
||||||
if !source_dir.exists() {
|
if !source_dir.exists() {
|
||||||
return Ok(InstallResult::MissingPackageVersion); // TODO: test
|
return Ok(InstallResult::MissingPackageVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_dir = &self.package_dir.join(name);
|
let target_dir = &self.package_dir.join(name);
|
||||||
|
@ -317,7 +315,7 @@ mod tests {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use crate::package::PackageManager;
|
use crate::package::PackageManager;
|
||||||
use std::fs::{create_dir, create_dir_all};
|
use std::fs::{create_dir, create_dir_all};
|
||||||
use crate::package::InstallResult::{Installed, NotFoundInRepo};
|
use crate::package::InstallResult::*;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
const OUTDATED_INDEX_CONTENT : &str = include_str!("../res/test/outdated_index.json");
|
const OUTDATED_INDEX_CONTENT : &str = include_str!("../res/test/outdated_index.json");
|
||||||
|
@ -496,6 +494,36 @@ mod tests {
|
||||||
assert_eq!(temp.package_manager.install_package("not-existing").unwrap(), NotFoundInRepo);
|
assert_eq!(temp.package_manager.install_package("not-existing").unwrap(), NotFoundInRepo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_package_missing_version() {
|
||||||
|
let mut temp = create_temp_package_manager(|_, data_dir| {
|
||||||
|
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
|
||||||
|
std::fs::write(index_file, INSTALL_PACKAGE_INDEX);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(temp.package_manager.install_package("dummy-package2").unwrap(), MissingPackageVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_package_missing_readme_unable_to_parse_package_info() {
|
||||||
|
let mut temp = create_temp_package_manager(|_, data_dir| {
|
||||||
|
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
|
||||||
|
std::fs::write(index_file, INSTALL_PACKAGE_INDEX);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(temp.package_manager.install_package("dummy-package3").unwrap(), UnableToParsePackageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_install_package_bad_readme_unable_to_parse_package_info() {
|
||||||
|
let mut temp = create_temp_package_manager(|_, data_dir| {
|
||||||
|
let index_file = data_dir.join(DEFAULT_PACKAGE_INDEX_FILE);
|
||||||
|
std::fs::write(index_file, INSTALL_PACKAGE_INDEX);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(temp.package_manager.install_package("dummy-package4").unwrap(), UnableToParsePackageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_local_packages() {
|
fn test_list_local_packages() {
|
||||||
let mut temp = create_temp_package_manager(|_, data_dir| {
|
let mut temp = create_temp_package_manager(|_, data_dir| {
|
||||||
|
|
BIN
src/res/linux/icon.png
Normal file
BIN
src/res/linux/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -12,6 +12,36 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "dummy-package2",
|
||||||
|
"title": "Dummy Package",
|
||||||
|
"version": "9.9.9",
|
||||||
|
"repo": "https://github.com/federico-terzi/espanso-hub-core",
|
||||||
|
"desc": "Dummy package",
|
||||||
|
"author": "Federico Terzi"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "dummy-package3",
|
||||||
|
"title": "Dummy Package",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"repo": "https://github.com/federico-terzi/espanso-hub-core",
|
||||||
|
"desc": "Dummy package",
|
||||||
|
"author": "Federico Terzi"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "dummy-package4",
|
||||||
|
"title": "Dummy Package",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"repo": "https://github.com/federico-terzi/espanso-hub-core",
|
||||||
|
"desc": "Dummy package",
|
||||||
|
"author": "Federico Terzi"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "italian-accents",
|
"name": "italian-accents",
|
||||||
|
|
|
@ -19,14 +19,20 @@
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use super::MenuItem;
|
use super::MenuItem;
|
||||||
use log::error;
|
use log::{error, info};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct LinuxUIManager {}
|
const LINUX_ICON_CONTENT : &[u8] = include_bytes!("../res/linux/icon.png");
|
||||||
|
|
||||||
|
pub struct LinuxUIManager {
|
||||||
|
icon_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
impl super::UIManager for LinuxUIManager {
|
impl super::UIManager for LinuxUIManager {
|
||||||
fn notify(&self, message: &str) {
|
fn notify(&self, message: &str) {
|
||||||
let res = Command::new("notify-send")
|
let res = Command::new("notify-send")
|
||||||
.args(&["-t", "2000", "espanso", message])
|
.args(&["-i", self.icon_path.to_str().unwrap_or_default(),
|
||||||
|
"-t", "2000", "espanso", message])
|
||||||
.output();
|
.output();
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
@ -45,6 +51,16 @@ impl super::UIManager for LinuxUIManager {
|
||||||
|
|
||||||
impl LinuxUIManager {
|
impl LinuxUIManager {
|
||||||
pub fn new() -> LinuxUIManager {
|
pub fn new() -> LinuxUIManager {
|
||||||
LinuxUIManager{}
|
// Initialize the icon if not present
|
||||||
|
let data_dir = crate::context::get_data_dir();
|
||||||
|
let icon_path = data_dir.join("icon.png");
|
||||||
|
if !icon_path.exists() {
|
||||||
|
info!("Creating espanso icon in '{}'", icon_path.to_str().unwrap_or_default());
|
||||||
|
std::fs::write(&icon_path, LINUX_ICON_CONTENT).expect("Unable to copy espanso icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxUIManager{
|
||||||
|
icon_path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user