Miscellaneous refactoring.

This commit is contained in:
Antoine POPINEAU 2021-07-18 10:40:28 +02:00
parent b178fa318b
commit b4e587a903
7 changed files with 162 additions and 129 deletions

View File

@ -12,7 +12,10 @@ use getopts::{Matches, Options};
use i18n_embed::DesktopLanguageRequester;
use tokio::{
net::UnixStream,
sync::{RwLock, RwLockWriteGuard},
sync::{
oneshot::{Receiver, Sender},
RwLock, RwLockWriteGuard,
},
};
use zeroize::Zeroize;
@ -81,8 +84,8 @@ pub struct Greeter {
pub working: bool,
pub done: bool,
#[default(Ok(()))]
pub exit: Result<(), AuthStatus>,
pub exit_tx: Option<Sender<AuthStatus>>,
pub exit_rx: Option<Receiver<AuthStatus>>,
}
impl Drop for Greeter {
@ -117,6 +120,10 @@ impl Greeter {
greeter.selected_session = greeter.sessions.iter().position(|(_, command)| Some(command) == greeter.command.as_ref()).unwrap_or(0);
let (exit_tx, exit_rx) = tokio::sync::oneshot::channel::<AuthStatus>();
greeter.exit_tx = Some(exit_tx);
greeter.exit_rx = Some(exit_rx);
greeter
}

View File

@ -8,105 +8,124 @@ use tokio::sync::{
use crate::{info::write_last_username, AuthStatus, Greeter, Mode};
type IpcChannel = (Arc<Mutex<Receiver<Request>>>, Arc<Mutex<Sender<Request>>>);
#[derive(Clone)]
pub struct Ipc(Arc<IpcHandle>);
pub fn new_ipc() -> IpcChannel {
let (net_tx, net_rx) = tokio::sync::mpsc::channel::<Request>(10);
(Arc::new(Mutex::new(net_rx)), Arc::new(Mutex::new(net_tx)))
pub struct IpcHandle {
tx: RwLock<Sender<Request>>,
rx: Mutex<Receiver<Request>>,
}
pub async fn handle(greeter: Arc<RwLock<Greeter>>, net_tx: Arc<Mutex<Sender<Request>>>, net_rx: Arc<Mutex<Receiver<Request>>>) -> Result<(), Box<dyn Error>> {
let request = net_rx.lock().await.recv().await;
impl Ipc {
pub fn new() -> Ipc {
let (tx, rx) = tokio::sync::mpsc::channel::<Request>(10);
if let Some(request) = request {
let stream = {
let greeter = greeter.read().await;
greeter.stream.as_ref().unwrap().clone()
};
let response = {
request.write_to(&mut *stream.write().await).await?;
Response::read_from(&mut *stream.write().await).await?
};
parse_response(&mut *greeter.write().await, response, net_tx).await?;
Ipc(Arc::new(IpcHandle {
tx: RwLock::new(tx),
rx: Mutex::new(rx),
}))
}
Ok(())
}
pub async fn send(&self, request: Request) {
let _ = self.0.tx.read().await.send(request).await;
}
async fn parse_response(mut greeter: &mut Greeter, response: Response, net_tx: Arc<Mutex<Sender<Request>>>) -> Result<(), Box<dyn Error>> {
match response {
Response::AuthMessage { auth_message_type, auth_message } => match auth_message_type {
AuthMessageType::Secret => {
greeter.mode = Mode::Password;
greeter.working = false;
greeter.secret = true;
greeter.set_prompt(&auth_message);
}
pub async fn next(&mut self) -> Option<Request> {
self.0.rx.lock().await.recv().await
}
AuthMessageType::Visible => {
greeter.mode = Mode::Password;
greeter.working = false;
greeter.secret = false;
greeter.set_prompt(&auth_message);
}
pub async fn handle(&mut self, greeter: Arc<RwLock<Greeter>>) -> Result<(), Box<dyn Error>> {
let request = self.next().await;
AuthMessageType::Error => greeter.message = Some(auth_message),
if let Some(request) = request {
let stream = {
let greeter = greeter.read().await;
AuthMessageType::Info => {
if let Some(message) = &mut greeter.message {
message.push_str(&auth_message.trim_end());
message.push('\n');
} else {
greeter.message = Some(auth_message.trim_end().to_string());
greeter.stream.as_ref().unwrap().clone()
};
let response = {
request.write_to(&mut *stream.write().await).await?;
Response::read_from(&mut *stream.write().await).await?
};
self.parse_response(&mut *greeter.write().await, response).await?;
}
Ok(())
}
async fn parse_response(&mut self, mut greeter: &mut Greeter, response: Response) -> Result<(), Box<dyn Error>> {
match response {
Response::AuthMessage { auth_message_type, auth_message } => match auth_message_type {
AuthMessageType::Secret => {
greeter.mode = Mode::Password;
greeter.working = false;
greeter.secret = true;
greeter.set_prompt(&auth_message);
}
AuthMessageType::Visible => {
greeter.mode = Mode::Password;
greeter.working = false;
greeter.secret = false;
greeter.set_prompt(&auth_message);
}
AuthMessageType::Error => greeter.message = Some(auth_message),
AuthMessageType::Info => {
if let Some(message) = &mut greeter.message {
message.push_str(&auth_message.trim_end());
message.push('\n');
} else {
greeter.message = Some(auth_message.trim_end().to_string());
if let Some(message) = &mut greeter.message {
message.push('\n');
}
}
self.send(Request::PostAuthMessageResponse { response: None }).await;
}
},
Response::Success => {
if greeter.done {
if greeter.remember {
write_last_username(&greeter.username);
}
crate::exit(&mut greeter, AuthStatus::Success).await;
} else if let Some(command) = &greeter.command {
greeter.done = true;
self.send(Request::StartSession { cmd: vec![command.clone()] }).await;
}
}
Response::Error { error_type, description } => {
Ipc::cancel(&mut greeter).await;
match error_type {
ErrorType::AuthError => {
greeter.message = Some(fl!("failed"));
}
ErrorType::Error => {
greeter.message = Some(description);
}
}
let _ = net_tx.lock().await.send(Request::PostAuthMessageResponse { response: None }).await;
}
},
Response::Success => {
if greeter.done {
if greeter.remember {
write_last_username(&greeter.username);
}
crate::exit(&mut greeter, AuthStatus::Success).await;
} else if let Some(command) = &greeter.command {
greeter.done = true;
let _ = net_tx.lock().await.send(Request::StartSession { cmd: vec![command.clone()] }).await;
greeter.reset().await;
}
}
Response::Error { error_type, description } => {
cancel(&mut greeter).await;
match error_type {
ErrorType::AuthError => {
greeter.message = Some(fl!("failed"));
}
ErrorType::Error => {
greeter.message = Some(description);
}
}
greeter.reset().await;
}
Ok(())
}
Ok(())
}
pub async fn cancel(greeter: &mut Greeter) {
let _ = Request::CancelSession.write_to(&mut *greeter.stream().await).await;
pub async fn cancel(greeter: &mut Greeter) {
let _ = Request::CancelSession.write_to(&mut *greeter.stream().await).await;
}
}

View File

@ -3,23 +3,23 @@ use std::{error::Error, sync::Arc};
use greetd_ipc::Request;
use system_shutdown::{reboot, shutdown};
use termion::event::Key;
use tokio::sync::{mpsc::Sender, Mutex, RwLock};
use tokio::sync::RwLock;
use crate::{
event::{Event, Events},
info::write_last_session,
ipc::cancel,
ipc::Ipc,
ui::{PowerOption, POWER_OPTIONS},
Greeter, Mode,
};
pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx: Arc<Mutex<Sender<Request>>>) -> Result<(), Box<dyn Error>> {
pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc) -> Result<(), Box<dyn Error>> {
if let Some(Event::Input(input)) = events.next().await {
let mut greeter = greeter.write().await;
match input {
Key::Esc => {
cancel(&mut greeter).await;
Ipc::cancel(&mut greeter).await;
greeter.reset().await;
}
@ -100,7 +100,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
greeter.working = true;
greeter.message = None;
let _ = net_tx.lock().await.send(Request::CreateSession { username: greeter.username.clone() }).await;
ipc.send(Request::CreateSession { username: greeter.username.clone() }).await;
greeter.answer = String::new();
}
@ -108,9 +108,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
greeter.working = true;
greeter.message = None;
let _ = net_tx
.lock()
.await
ipc
.send(Request::PostAuthMessageResponse {
response: Some(greeter.answer.clone()),
})
@ -172,7 +170,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
#[cfg(debug_assertions)]
Key::Ctrl('x') => {
use crate::config::AuthStatus;
use crate::greeter::AuthStatus;
crate::exit(&mut greeter, AuthStatus::Cancel).await;
}

View File

@ -1,9 +1,9 @@
macro_rules! fl {
($message_id:literal) => {{
i18n_embed_fl::fl!($crate::MESSAGES, $message_id)
i18n_embed_fl::fl!($crate::ui::MESSAGES, $message_id)
}};
($message_id:literal, $($args:expr),*) => {{
i18n_embed_fl::fl!($crate::MESSAGES, $message_id, $($args), *)
i18n_embed_fl::fl!($crate::ui::MESSAGES, $message_id, $($args), *)
}};
}

View File

@ -4,8 +4,8 @@ extern crate smart_default;
#[macro_use]
mod macros;
mod config;
mod event;
mod greeter;
mod info;
mod ipc;
mod keyboard;
@ -14,34 +14,12 @@ mod ui;
use std::{error::Error, io, process, sync::Arc};
use greetd_ipc::Request;
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DesktopLanguageRequester, LanguageLoader,
};
use lazy_static::lazy_static;
use rust_embed::RustEmbed;
use termion::raw::IntoRawMode;
use tokio::sync::RwLock;
use tui::{backend::TermionBackend, Terminal};
pub use self::config::*;
use self::{event::Events, ipc::new_ipc};
#[derive(RustEmbed)]
#[folder = "contrib/locales"]
struct Localizations;
lazy_static! {
static ref MESSAGES: FluentLanguageLoader = {
let locales = Localizations;
let loader = fluent_language_loader!();
loader.load_languages(&locales, &[loader.fallback_language()]).unwrap();
let _ = i18n_embed::select(&loader, &locales, &DesktopLanguageRequester::requested_languages());
loader
};
}
pub use self::greeter::*;
use self::{event::Events, ipc::Ipc};
#[tokio::main]
async fn main() {
@ -64,43 +42,48 @@ async fn run() -> Result<(), Box<dyn Error>> {
terminal.clear()?;
let mut events = Events::new().await;
let (ipc_rx, ipc_tx) = new_ipc();
let ipc = Ipc::new();
if greeter.remember && !greeter.username.is_empty() {
let _ = ipc_tx.lock().await.send(Request::CreateSession { username: greeter.username.clone() }).await;
ipc.send(Request::CreateSession { username: greeter.username.clone() }).await;
}
let greeter = Arc::new(RwLock::new(greeter));
tokio::task::spawn({
let greeter = greeter.clone();
let (ipc_rx, ipc_tx) = (ipc_rx.clone(), ipc_tx.clone());
let mut ipc = ipc.clone();
async move {
loop {
let _ = ipc::handle(greeter.clone(), ipc_tx.clone(), ipc_rx.clone()).await;
let _ = ipc.handle(greeter.clone()).await;
}
}
});
loop {
greeter.read().await.exit?;
if let Some(ref mut rx) = greeter.write().await.exit_rx {
if let Ok(status) = rx.try_recv() {
return Err(status.into());
}
}
ui::draw(greeter.clone(), &mut terminal).await?;
keyboard::handle(greeter.clone(), &mut events, ipc_tx.clone()).await?;
keyboard::handle(greeter.clone(), &mut events, ipc.clone()).await?;
}
}
pub async fn exit(mut greeter: &mut Greeter, status: AuthStatus) {
match status {
AuthStatus::Success => {}
AuthStatus::Cancel | AuthStatus::Failure => ipc::cancel(&mut greeter).await,
AuthStatus::Cancel | AuthStatus::Failure => Ipc::cancel(&mut greeter).await,
}
clear_screen();
greeter.exit = Err(status);
if let Some(tx) = greeter.exit_tx.take() {
let _ = tx.send(status);
}
}
pub fn clear_screen() {

22
src/ui/i18n.rs Normal file
View File

@ -0,0 +1,22 @@
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DesktopLanguageRequester, LanguageLoader,
};
use lazy_static::lazy_static;
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "contrib/locales"]
struct Localizations;
lazy_static! {
pub static ref MESSAGES: FluentLanguageLoader = {
let locales = Localizations;
let loader = fluent_language_loader!();
loader.load_languages(&locales, &[loader.fallback_language()]).unwrap();
let _ = i18n_embed::select(&loader, &locales, &DesktopLanguageRequester::requested_languages());
loader
};
}

View File

@ -1,4 +1,5 @@
mod command;
mod i18n;
mod power;
mod prompt;
mod sessions;
@ -24,7 +25,10 @@ use tui::{
use crate::{info::capslock_status, ui::util::titleize, Greeter, Mode};
pub use self::power::{Option as PowerOption, OPTIONS as POWER_OPTIONS};
pub use self::{
i18n::MESSAGES,
power::{Option as PowerOption, OPTIONS as POWER_OPTIONS},
};
const TITLEBAR_INDEX: usize = 1;
const STATUSBAR_INDEX: usize = 3;