Skip to content
Extraits de code Groupes Projets
Vérifiée Valider a458c6a7 rédigé par Kubat's avatar Kubat
Parcourir les fichiers

AMADEUS: The decode of the lektord response won't panic anymore on invalid...

AMADEUS: The decode of the lektord response won't panic anymore on invalid input + less spagetti code

Signed-off-by: default avatarKubat <mael.martin31@gmail.com>
parent 3290c567
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!193AMADEUS: Implementation of lkt-lib
Pipeline #3228 en échec
Ce commit fait partie de la requête de fusion !193. Les commentaires créés ici seront créés dans le contexte de cette requête de fusion.
#![allow(dead_code)]
use lkt_lib::{query::LektorQuery, response::LektorFormatedResponse};
use lkt_lib::{query::*, response::LektorFormatedResponse};
use log::error;
use std::{
cell::Cell,
......@@ -114,15 +114,10 @@ impl Deamon for CommandDeamon {
continue;
}
};
match lkt_lib::response::LektorFormatedResponse::try_from(res) {
Ok(response) => {
if let Err(e) = responses_send.send(response) {
if let Err(e) = responses_send.send(res) {
error!("failed to send response to amadeus: {e}")
}
}
Err(e) => error!("failed to format the response: {e}"),
}
}
Err(e) => error!("failed to get command from amadeus: {e}"),
};
});
......@@ -158,33 +153,34 @@ impl Deamon for StatusDeamon {
return_when_flagged!(quit_deamon, joined_deamon);
let status = {
let res = match connexion.send_query(lkt_lib::query::LektorQuery::PlaybackStatus) {
let mut res = match connexion.send_query(LektorQuery::PlaybackStatus) {
Ok(res) => res,
Err(e) => {
error!("failed to send the playback status command to lektor: {e}");
continue;
}
};
match lkt_lib::response::LektorFormatedResponse::try_from(res) {
Err(e) => panic!("{e}"),
Ok(mut response) => lkt_lib::response::PlaybackStatus::consume(&mut response),
match lkt_lib::response::PlaybackStatus::consume(&mut res) {
Err(e) => {
error!("failed to build response from formated response: {e}");
continue;
}
Ok(res) => res,
}
};
let current = if status.state != lkt_lib::types::LektorState::Stopped {
let res = match connexion.send_query(lkt_lib::query::LektorQuery::CurrentKara) {
let mut res = match connexion.send_query(lkt_lib::query::LektorQuery::CurrentKara) {
Ok(res) => res,
Err(e) => {
error!("failed to send the current kara command to lektor: {e}",);
continue;
}
};
match lkt_lib::response::LektorFormatedResponse::try_from(res) {
Ok(mut response) => {
Some(lkt_lib::response::CurrentKara::consume(&mut response))
}
Err(e) => {
error!("failed to format response from lektor: {e}");
match lkt_lib::response::CurrentKara::consume(&mut res) {
Ok(res) => Some(res),
Err(err) => {
error!("failed to build response from formated response: {err}");
None
}
}
......@@ -193,7 +189,7 @@ impl Deamon for StatusDeamon {
};
if let Err(e) = responses_send.send((status, current)) {
error!("Failed to send a status response to amadeus: {}", e);
error!("Failed to send a status response to amadeus: {e}");
}
});
......
//! Create a connexion to a lektord instande then send commands and recieve data
//! from the server.
use crate::{constants, query::LektorQuery, query::LektorQueryLineType};
use crate::{
constants, query::LektorQuery, query::LektorQueryLineType, response::LektorFormatedResponse,
};
use log::error;
use std::{
io::{self, BufRead, BufReader, Write},
......@@ -9,6 +11,21 @@ use std::{
str::FromStr,
};
pub enum LektorQueryError {
IO(io::Error),
Other(String),
}
impl std::fmt::Display for LektorQueryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use LektorQueryError::*;
match self {
IO(io) => write!(f, "lektor query error io: {io}"),
Other(other) => write!(f, "lektor query error: {other}"),
}
}
}
pub struct LektorConnexion {
stream: TcpStream,
pub version: String,
......@@ -54,10 +71,14 @@ impl LektorConnexion {
}
}
pub fn send_query(&mut self, query: LektorQuery) -> io::Result<Vec<String>> {
pub fn send_query(
&mut self,
query: LektorQuery,
) -> Result<LektorFormatedResponse, LektorQueryError> {
let mut res: Vec<String> = Vec::new();
self.send_query_inner(query, &mut res)?;
Ok(res)
self.send_query_inner(query, &mut res)
.map_err(LektorQueryError::IO)?;
LektorFormatedResponse::try_from(res).map_err(LektorQueryError::Other)
}
fn send_query_inner(
......
......@@ -3,6 +3,7 @@
pub mod connexion;
mod constants;
mod macros;
pub mod query;
pub mod response;
pub mod types;
......
#[macro_export]
macro_rules! then_some {
($cond: expr => $some: expr) => {
if $cond {
Some($some)
} else {
None
}
};
}
//! Contains types for typed response.
use crate::then_some;
use crate::types::LektorState;
use std::collections::HashMap;
#[derive(Debug)]
pub struct LektorFormatedResponse {
content: HashMap<String, String>,
content: Vec<(String, String)>,
}
impl LektorFormatedResponse {
pub fn pop(&mut self, key: &str) -> String {
match self.content.remove(key) {
Some(x) => x,
None => panic!("Failed to find a value with the key {key} in the formated response"),
pub fn pop(&mut self, key: &str) -> Result<String, String> {
match self
.content
.iter()
.enumerate()
.find_map(|(index, (what, _))| then_some!(what == key => index))
{
Some(index) => Ok(self.content.remove(index).1),
None => Err(format!("no key {key} was found in formated response")),
}
}
}
impl IntoIterator for LektorFormatedResponse {
type Item = (String, String);
type IntoIter =
<std::vec::Vec<(std::string::String, std::string::String)> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.content.into_iter()
}
}
impl TryFrom<Vec<String>> for LektorFormatedResponse {
type Error = String;
fn try_from(vec: Vec<String>) -> Result<Self, Self::Error> {
let mut content = HashMap::with_capacity(vec.len());
let mut content = Vec::with_capacity(vec.len());
for line in vec {
let key_pair: Vec<&str> = line.splitn(2, ':').collect();
match key_pair[..] {
[key, value] => {
content.insert(key.trim().to_lowercase(), value.trim().to_string());
content.push((key.trim().to_lowercase(), value.trim().to_string()));
}
_ => return Err(format!("Invalid line for formated response: {}", line)),
}
......@@ -58,38 +73,38 @@ pub struct PlaybackStatus {
}
impl PlaybackStatus {
pub fn consume(response: &mut LektorFormatedResponse) -> Self {
pub fn consume(response: &mut LektorFormatedResponse) -> Result<Self, String> {
let mut ret = Self {
elapsed: response.pop("elapsed").parse::<usize>().unwrap_or(0),
songid: match response.pop("songid").parse::<isize>() {
elapsed: response.pop("elapsed")?.parse::<usize>().unwrap_or(0),
songid: match response.pop("songid")?.parse::<isize>() {
Ok(x) if x <= 0 => None,
Ok(x) => Some(x as usize),
Err(_) => None,
},
song: match response.pop("song").parse::<usize>() {
song: match response.pop("song")?.parse::<usize>() {
Ok(x) => Some(x),
Err(_) => None,
},
volume: response
.pop("volume")
.pop("volume")?
.parse::<usize>()
.unwrap()
.clamp(0, 100),
duration: response.pop("duration").parse::<usize>().unwrap(),
updating_db: response.pop("updating_db").parse::<usize>().unwrap(),
playlistlength: response.pop("playlistlength").parse::<usize>().unwrap(),
random: response.pop("random").parse::<usize>().unwrap() != 0,
consume: response.pop("consume").parse::<usize>().unwrap() != 0,
single: response.pop("single").parse::<usize>().unwrap() != 0,
repeat: response.pop("repeat").parse::<usize>().unwrap() != 0,
duration: response.pop("duration")?.parse::<usize>().unwrap(),
updating_db: response.pop("updating_db")?.parse::<usize>().unwrap(),
playlistlength: response.pop("playlistlength")?.parse::<usize>().unwrap(),
random: response.pop("random")?.parse::<usize>().unwrap() != 0,
consume: response.pop("consume")?.parse::<usize>().unwrap() != 0,
single: response.pop("single")?.parse::<usize>().unwrap() != 0,
repeat: response.pop("repeat")?.parse::<usize>().unwrap() != 0,
state: LektorState::Stopped,
};
ret.state = match &response.pop("state")[..] {
ret.state = match &response.pop("state")?[..] {
"play" => LektorState::Play(ret.songid.unwrap()),
"pause" => LektorState::Pause(ret.songid.unwrap()),
_ => LektorState::Stopped,
};
ret
Ok(ret)
}
}
......@@ -105,8 +120,8 @@ pub struct CurrentKara {
}
impl CurrentKara {
pub fn consume(response: &mut LektorFormatedResponse) -> Self {
let song_type_number = response.pop("type");
pub fn consume(response: &mut LektorFormatedResponse) -> Result<Self, String> {
let song_type_number = response.pop("type")?;
let (song_type, song_number) = match song_type_number.find(char::is_numeric) {
Some(index) => (
(&song_type_number[..index]).to_owned(),
......@@ -118,14 +133,14 @@ impl CurrentKara {
None => panic!("Oupsy"),
};
Self {
title: response.pop("title"),
author: response.pop("author"),
source: response.pop("source"),
category: response.pop("category"),
language: response.pop("language"),
Ok(Self {
title: response.pop("title")?,
author: response.pop("author")?,
source: response.pop("source")?,
category: response.pop("category")?,
language: response.pop("language")?,
song_type,
song_number,
}
})
}
}
......@@ -4,21 +4,19 @@ fn main() {
let mut lektor = LektorConnexion::new("localhost".to_owned(), 6600).unwrap();
if lektor.send_query(LektorQuery::Ping).is_ok() {}
if let Ok(res) = lektor.send_query(LektorQuery::CurrentKara) {
let mut response = response::LektorFormatedResponse::try_from(res).unwrap();
if let Ok(mut response) = lektor.send_query(LektorQuery::CurrentKara) {
let current_kara = response::CurrentKara::consume(&mut response);
println!("CURRENT {:?}", current_kara);
}
if let Ok(res) = lektor.send_query(LektorQuery::PlaybackStatus) {
let mut response = response::LektorFormatedResponse::try_from(res).unwrap();
if let Ok(mut response) = lektor.send_query(LektorQuery::PlaybackStatus) {
let playback_status = response::PlaybackStatus::consume(&mut response);
println!("PLAYBACK-STATUS {:?}", playback_status);
}
if let Ok(res) = lektor.send_query(LektorQuery::ListAllPlaylists) {
for item in res {
println!("ALL PLAYLISTS {}", item);
if let Ok(response) = lektor.send_query(LektorQuery::ListAllPlaylists) {
for (what, item) in response.into_iter() {
println!("ALL PLAYLISTS {what}:{item}");
}
}
}
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter