From e12f15dc0d10a71f54a4c758d13549183dec8963 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Tue, 10 Jan 2023 17:11:22 +0100
Subject: [PATCH] RUST: Continue development of amalib and lkt-rs

Fixed bugged communications of amalib. Results where not correctly
parsed with the new async way of doing things.
---
 src/rust/amadeus-next/amalib/src/connexion.rs | 120 +++++++-----------
 src/rust/amadeus-next/amalib/src/query.rs     |  77 ++++++-----
 src/rust/amadeus-next/amalib/src/response.rs  |   2 +-
 src/rust/amadeus-next/lkt-rs/src/args.rs      |  16 +--
 src/rust/amadeus-next/lkt-rs/src/config.rs    |  68 ++++------
 src/rust/amadeus-next/lkt-rs/src/main.rs      | 113 +++++++++++------
 6 files changed, 195 insertions(+), 201 deletions(-)

diff --git a/src/rust/amadeus-next/amalib/src/connexion.rs b/src/rust/amadeus-next/amalib/src/connexion.rs
index 29de6762..1e2f9bdc 100644
--- a/src/rust/amadeus-next/amalib/src/connexion.rs
+++ b/src/rust/amadeus-next/amalib/src/connexion.rs
@@ -28,6 +28,7 @@ async fn write_string(
         err_report!(err BufferDontEndWithLF)
     } else {
         loop {
+            trace!("try to write {} bytes...", buffer.len());
             stream.writable().await.map_err(Io)?;
             match stream.try_write(buffer.as_bytes()) {
                 Ok(n) if n != size => break err_report!(err IncorrectWriteSize(size, n)),
@@ -39,79 +40,46 @@ async fn write_string(
     }
 }
 
-/// Read replies from the lektord server.
+/// Read replies from the lektord server. If the `exit_on_would_block` is set,
+/// and if nothing is available returns nothing!
 async fn read_replies(
     stream: &mut TcpStream,
+    exit_on_would_block: bool,
 ) -> StackedResult<(Vec<String>, Option<usize>), LektorCommError> {
     let (mut ret, mut continuation) = (Vec::new(), None);
+    let peer_addr = stream.peer_addr().expect("failed to get peer address");
     loop {
-        stream
-                .readable().await
-                .map_err(|e| err_report!(LektorCommError::Io(e) => [ format!("failed to get MPD version from {:?}", stream.peer_addr()) ]))?;
+        trace!("try to read from remote...");
+        stream.readable().await.map_err(|e| {
+            err_report!(LektorCommError::Io(e) => [
+                format!("failed to read from {peer_addr}") ])
+        })?;
         let mut line = [0; constants::LKT_MESSAGE_MAX];
         match stream.try_read(&mut line) {
-            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
-            Err(e) => return err_report!(err LektorCommError::Io(e)),
-            Ok(0) => {
-                return err_report!(err LektorCommError::Io(std::io::Error::new(std::io::ErrorKind::Other, "recieved empty line")))
-            }
-            Ok(size) => {
-                let msg = std::str::from_utf8(&line[..size])
-                        .map_err(|e| err_report!(LektorCommError::Utf8(e) => [ format!("the lektord server at {:?} returned an invalid utf8 string", stream.peer_addr()) ]))?
-                        .trim();
-                match LektorQueryLineType::from_str(msg) {
-                    Ok(LektorQueryLineType::Ok) => return Ok((ret, continuation)),
-                    Ok(LektorQueryLineType::Ack) => {
-                        return err_report!(err LektorCommError::Io(std::io::Error::from(
-                            std::io::ErrorKind::Other
-                        )))
-                    }
-                    Ok(LektorQueryLineType::Data) => ret.push(msg.to_string()),
-                    Ok(LektorQueryLineType::ListOk) => continue,
-                    Ok(LektorQueryLineType::Continuation(cont)) => continuation = Some(cont),
-                    Err(()) => {
-                        return err_report!(err LektorCommError::QueryError => [ "unknown query line type" ])
-                    }
-                }
+            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
+                either!(exit_on_would_block => return Ok((vec![], None)); continue)
             }
-        }
-    }
-}
-
-/// Read replies from the lektord server. If nothing is available returns
-/// nothing! This function do the same thing as [read_replies] but breaks on
-/// [std::io::ErrorKind::WouldBlock].
-async fn read_maybe_replies(
-    stream: &mut TcpStream,
-) -> StackedResult<Option<(Vec<String>, Option<usize>)>, LektorCommError> {
-    let (mut ret, mut continuation) = (Vec::new(), None);
-    loop {
-        stream
-                .readable().await
-                .map_err(|e| err_report!(LektorCommError::Io(e) => [ format!("failed to get MPD version from {:?}", stream.peer_addr()) ]))?;
-        let mut line = [0; constants::LKT_MESSAGE_MAX];
-        match stream.try_read(&mut line) {
-            Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => return Ok(None),
             Err(e) => return err_report!(err LektorCommError::Io(e)),
-            Ok(0) => {
-                return err_report!(err LektorCommError::Io(std::io::Error::new(std::io::ErrorKind::Other, "recieved empty line")))
-            }
+            Ok(0) => return Ok((ret, continuation)),
             Ok(size) => {
-                let msg = std::str::from_utf8(&line[..size])
-                        .map_err(|e| err_report!(LektorCommError::Utf8(e) => [ format!("the lektord server at {:?} returned an invalid utf8 string", stream.peer_addr()) ]))?
-                        .trim();
-                match LektorQueryLineType::from_str(msg) {
-                    Ok(LektorQueryLineType::Ok) => return Ok(Some((ret, continuation))),
-                    Ok(LektorQueryLineType::Ack) => {
-                        return err_report!(err LektorCommError::Io(std::io::Error::from(
-                            std::io::ErrorKind::Other
-                        )))
-                    }
-                    Ok(LektorQueryLineType::Data) => ret.push(msg.to_string()),
-                    Ok(LektorQueryLineType::ListOk) => continue,
-                    Ok(LektorQueryLineType::Continuation(cont)) => continuation = Some(cont),
-                    Err(()) => {
-                        return err_report!(err LektorCommError::QueryError => [ "unknown query line type" ])
+                let lines = std::str::from_utf8(&line[..size]).map_err(|e|
+                    err_report!(LektorCommError::Utf8(e) => [
+                        format!("the lektord server at {peer_addr} returned an invalid utf8 string")
+                    ])
+                )?.trim().lines().map(|line| line.trim());
+                for msg in lines {
+                    trace!("got line: {msg}");
+                    match LektorQueryLineType::from_str(msg) {
+                        Ok(LektorQueryLineType::Ok) => return Ok((ret, continuation)),
+                        Ok(LektorQueryLineType::Ack(msg)) => {
+                            return err_report!(err LektorCommError::MpdAck => [ msg ])
+                        }
+                        Ok(LektorQueryLineType::Data) => ret.push(msg.to_string()),
+                        Ok(LektorQueryLineType::ListOk) => continue,
+                        Ok(LektorQueryLineType::Continuation(cont)) => continuation = Some(cont),
+                        Err(()) => {
+                            return err_report!(err LektorCommError::QueryError => [ "unknown query line type" ])
+                        }
                     }
                 }
             }
@@ -195,6 +163,12 @@ impl LektorConnexion {
         &mut self,
         query: LektorQuery,
     ) -> StackedResult<LektorResponse, LektorCommError> {
+        debug!(
+            "send query to server {}: {query:?}",
+            self.stream
+                .peer_addr()
+                .expect("failed to get remote address")
+        );
         let mut res: Vec<String> = Vec::new();
         query
             .verify()
@@ -213,22 +187,21 @@ impl LektorConnexion {
         query: LektorQuery,
         previous_ret: &mut Vec<String>,
     ) -> StackedResult<(), LektorCommError> {
-        write_string(&mut self.stream, query.to_string()).await?;
+        write_string(&mut self.stream, query.format_query()).await?;
         loop {
-            match read_replies(&mut self.stream).await {
+            match read_replies(&mut self.stream, false).await {
                 Err(e) => return Err(e),
                 Ok((res, _)) if res.is_empty() => return Ok(()),
                 Ok((res, None)) => {
+                    trace!("got no continuation and return: {res:?}");
                     previous_ret.extend(res);
                     return Ok(());
                 }
                 Ok((res, Some(cont))) => {
+                    trace!("got continuation {cont} and return: {res:?}");
                     previous_ret.extend(res);
-                    write_string(
-                        &mut self.stream,
-                        LektorQuery::create_continuation(query.clone(), cont).to_string(),
-                    )
-                    .await?;
+                    let query = LektorQuery::create_continuation(query.clone(), cont);
+                    write_string(&mut self.stream, query.format_query()).await?;
                 }
             }
         }
@@ -252,7 +225,7 @@ impl LektorIdleConnexion {
 
         write_string(&mut stream, idle_list_buffer).await?;
 
-        let (mut reply, _) = read_replies(&mut stream).await?;
+        let (mut reply, _) = read_replies(&mut stream, false).await?;
         either!(reply.is_empty()
             => Ok(Self { version, stream, idle_list })
             ; {
@@ -264,9 +237,8 @@ impl LektorIdleConnexion {
     }
 
     pub async fn get_notifications(&mut self) -> Vec<LektorIdleNotification> {
-        match read_maybe_replies(&mut self.stream).await {
-            Ok(None) => vec![],
-            Ok(Some((notifications, _))) => notifications
+        match read_replies(&mut self.stream, true).await {
+            Ok((notifications, _)) => notifications
                 .iter()
                 .filter_map(|msg| msg.strip_prefix("changed: "))
                 .flat_map(|notifications| notifications.split(&[',', ' '][..]))
diff --git a/src/rust/amadeus-next/amalib/src/query.rs b/src/rust/amadeus-next/amalib/src/query.rs
index fda3e8d1..4aeab97c 100644
--- a/src/rust/amadeus-next/amalib/src/query.rs
+++ b/src/rust/amadeus-next/amalib/src/query.rs
@@ -3,10 +3,11 @@
 use crate::*;
 use std::string::ToString;
 
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub(crate) enum LektorQueryLineType {
     Ok,
     ListOk,
-    Ack,
+    Ack(String),
     Continuation(usize),
     Data,
 }
@@ -16,6 +17,11 @@ pub enum LektorQuery {
     Ping,
     Close,
     KillServer,
+    RestartServer,
+    Update,
+    DryUpdate,
+    Populate,
+    DryPopulate,
     ConnectAsUser(String, Box<LektorQuery>),
 
     CurrentKara,
@@ -48,7 +54,7 @@ impl std::str::FromStr for LektorQueryLineType {
         if Self::is_line_ok(line) {
             Ok(Self::Ok)
         } else if Self::is_line_ack(line) {
-            Ok(Self::Ack)
+            Ok(Self::Ack(line.to_string()))
         } else if Self::is_line_list_ok(line) {
             Ok(Self::ListOk)
         } else if let Some(cont) = Self::is_line_continuation(line) {
@@ -79,7 +85,7 @@ impl LektorQueryLineType {
     }
 
     fn is_line_ack(line: &str) -> bool {
-        line.starts_with("ACK: ")
+        line.starts_with("ACK [")
     }
 }
 
@@ -90,10 +96,11 @@ type QueryToTypeResponseBuilder = fn(LektorFormatedResponse) -> Result<LektorRes
 impl LektorQuery {
     /// Get the function to use to produce the typed response from the formated
     /// response, to automatically create the correct thing.
-    pub fn get_response_type(&self) -> QueryToTypeResponseBuilder {
+    pub(crate) fn get_response_type(&self) -> QueryToTypeResponseBuilder {
         use LektorQuery::*;
         match self {
-            Ping | Close | KillServer | PlayNext | PlayPrevious | ShuffleQueue | InsertKara(_)
+            Populate | DryPopulate | Update | DryUpdate | Ping | Close | KillServer
+            | RestartServer | PlayNext | PlayPrevious | ShuffleQueue | InsertKara(_)
             | AddKara(_) => LektorEmptyResponse::from_formated,
 
             CreatePlaylist(_)
@@ -115,7 +122,7 @@ impl LektorQuery {
 
     /// Create a continued query out of another one. If the query is already a
     /// continuation query then the underlying query is reused.
-    pub fn create_continuation(query: Self, cont: usize) -> Self {
+    pub(crate) fn create_continuation(query: Self, cont: usize) -> Self {
         match query {
             Self::Continuation(_, query) => Self::Continuation(cont, query),
             _ => Self::Continuation(cont, Box::new(query)),
@@ -123,8 +130,13 @@ impl LektorQuery {
     }
 
     /// Verify that a query is Ok.
-    pub fn verify(&self) -> Result<(), String> {
+    pub(crate) fn verify(&self) -> Result<(), String> {
         use LektorQuery::*;
+        macro_rules! admin_commands {
+            () => {
+                Populate | DryPopulate | DryUpdate | Update | KillServer | RestartServer | Close
+            };
+        }
         match self {
             // User commands
             SearchKara(_) | FindAddKara(_) | InsertKara(_) | AddKara(_) | PlaybackStatus
@@ -139,12 +151,11 @@ impl LektorQuery {
             }
 
             // Should be admin commands
-            Close => Err("close is an admin command".to_string()),
-            KillServer => Err("kill server is an admin command".to_string()),
+            admin_commands!() => Err("admin command".to_string()),
 
             // Admin commands
             ConnectAsUser(_, cmd) => match cmd.as_ref() {
-                Close | KillServer => Ok(()),
+                admin_commands!() => Ok(()),
                 _ => Err(format!("not an admin command: {cmd:?}")),
             },
 
@@ -155,30 +166,30 @@ impl LektorQuery {
             },
         }
     }
-}
-
-impl std::fmt::Display for LektorQuery {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        macro_rules! lkt_str {
-            ($lit:literal) => {
-                f.write_str(concat!($lit, '\n'))
-            };
-        }
 
+    /// Format the query to send it to the lektor server.
+    pub(crate) fn format_query(&self) -> String {
+        #[rustfmt::skip]
+        macro_rules! lkt_str { ($lit:literal) => { (concat!($lit, '\n')).to_string() }; }
         use LektorQuery::*;
         match self {
+            Populate => lkt_str!("rescan"),
+            DryPopulate => lkt_str!("__dry_rescan"),
+            Update => lkt_str!("update"),
+            DryUpdate => lkt_str!("__dry_update"),
             Ping => lkt_str!("ping"),
             Close => lkt_str!("close"),
             KillServer => lkt_str!("kill"),
-            ConnectAsUser(password, cmd) => write!(
-                f,
+            RestartServer => lkt_str!("__restart"),
+            ConnectAsUser(password, cmd) => format!(
                 concat!(
                     "command_list_ok_begin\n",
                     "password {}\n",
-                    "{}\n",
+                    "{}",
                     "command_list_end\n",
                 ),
-                password, cmd
+                password,
+                cmd.format_query()
             ),
 
             CurrentKara => lkt_str!("currentsong"),
@@ -189,19 +200,19 @@ impl std::fmt::Display for LektorQuery {
             ShuffleQueue => lkt_str!("shuffle"),
 
             ListAllPlaylists => lkt_str!("listplaylists"),
-            ListPlaylist(plt_name) => writeln!(f, "listplaylist {plt_name}"),
-            SearchKara(uri) => writeln!(f, "find {uri}"),
+            ListPlaylist(plt_name) => format!("listplaylist {plt_name}\n"),
+            SearchKara(uri) => format!("find {uri}\n"),
 
-            FindAddKara(uri) => writeln!(f, "findadd {uri}"),
-            InsertKara(uri) => writeln!(f, "__insert {uri}"),
-            AddKara(uri) => writeln!(f, "add {uri}"),
+            FindAddKara(uri) => format!("findadd {uri}\n"),
+            InsertKara(uri) => format!("__insert {uri}\n"),
+            AddKara(uri) => format!("add {uri}\n"),
 
-            CreatePlaylist(name) => todo!("create playlist {name}"),
-            DestroyPlaylist(name) => todo!("destroy playlist {name}"),
-            AddToPlaylist(name, uri) => todo!("add to playlist {name} {uri}"),
-            RemoveFromPlaylist(name, uri) => todo!("remove from playlist {name} {uri}"),
+            CreatePlaylist(name) => format!("playlistadd {name}\n"),
+            DestroyPlaylist(name) => format!("playlistdelete {name}\n"),
+            AddToPlaylist(name, uri) => format!("playlistadd {name} {uri}\n"),
+            RemoveFromPlaylist(name, uri) => format!("playlistdelete {name} {uri}\n"),
 
-            Continuation(cont, query) => write!(f, "{cont} {query}"),
+            Continuation(cont, query) => format!("{cont} {}\n", query.format_query()),
         }
     }
 }
diff --git a/src/rust/amadeus-next/amalib/src/response.rs b/src/rust/amadeus-next/amalib/src/response.rs
index 75e53f74..626eea26 100644
--- a/src/rust/amadeus-next/amalib/src/response.rs
+++ b/src/rust/amadeus-next/amalib/src/response.rs
@@ -172,7 +172,7 @@ impl LektorCurrentKaraResponse {
     }
 
     pub fn into_inner(self) -> LektorCurrentKaraInnerResponse {
-        self.content.unwrap()
+        self.content.expect("no current kara playing")
     }
 }
 
diff --git a/src/rust/amadeus-next/lkt-rs/src/args.rs b/src/rust/amadeus-next/lkt-rs/src/args.rs
index 85518495..c9325d1c 100644
--- a/src/rust/amadeus-next/lkt-rs/src/args.rs
+++ b/src/rust/amadeus-next/lkt-rs/src/args.rs
@@ -327,7 +327,6 @@ pub enum LktCommand {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum LktQueueCommand {
     ShowCurrent,
-
     ShowStatus,
 
     List {
@@ -335,20 +334,15 @@ pub enum LktQueueCommand {
     },
 
     Next,
-
     Previous,
 
-    Play {
-        index: usize,
-    },
-
     Pause,
-
     UnPause,
-
     TogglePause,
-
     Stop,
+    Play {
+        index: usize,
+    },
 
     Add {
         priority: PriorityLevel,
@@ -356,7 +350,6 @@ pub enum LktQueueCommand {
     },
 
     Crop,
-
     Clear {
         up_to_lvl: PriorityLevel,
     },
@@ -464,7 +457,8 @@ impl LktCommand {
             SubCommand::Queue { shuffle: Some(lvl), .. } => Queue(LktQueueCommand::Shuffle { up_to_lvl: lvl.unwrap_or(PriorityLevel::Enforce) }),
             SubCommand::Queue { clear:   Some(lvl), .. } => Queue(LktQueueCommand::Clear   { up_to_lvl: lvl.unwrap_or(PriorityLevel::Enforce) }),
             SubCommand::Queue { seek:    Some(id), .. } => { Queue(LktQueueCommand::SeekIdInQueue { id }) }
-            SubCommand::Queue { pos:     Some(range), .. } => Queue(LktQueueCommand::List { range: range.unwrap_or_else(|| 0..usize::MAX) }),
+            SubCommand::Queue { pos:     Some(None), .. } => Queue(LktQueueCommand::List { range: 0..config.search.default_queue_count }),
+            SubCommand::Queue { pos:     Some(Some(range)), .. } => Queue(LktQueueCommand::List { range }),
             SubCommand::Queue { play:    Some(index), .. } => Queue(LktQueueCommand::Play { index: index.unwrap_or_default() }),
             SubCommand::Queue { swap:    Some(args), .. } => match &args[..] {
                 [p1, p2] => Queue(LktQueueCommand::SwapPositions {
diff --git a/src/rust/amadeus-next/lkt-rs/src/config.rs b/src/rust/amadeus-next/lkt-rs/src/config.rs
index d0ea7f9c..67115c07 100644
--- a/src/rust/amadeus-next/lkt-rs/src/config.rs
+++ b/src/rust/amadeus-next/lkt-rs/src/config.rs
@@ -9,14 +9,9 @@ pub enum LktHostPort {
 }
 
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
-pub struct LktAdminConfig {
-    pub user: Option<String>,
-    pub password: Option<String>,
-}
-
-#[derive(Debug, serde::Deserialize, serde::Serialize, Default)]
 pub struct LktSearchConfig {
     pub query_type: LektorQueryType,
+    pub default_queue_count: usize,
 }
 
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
@@ -28,16 +23,25 @@ pub struct LktHostConfig {
 #[derive(Debug, serde::Deserialize, serde::Serialize)]
 pub struct LktConfig {
     pub host: LktHostConfig,
-    pub admin: Option<LktAdminConfig>,
+    pub password: Option<String>,
     pub search: LktSearchConfig,
 }
 
+impl Default for LktSearchConfig {
+    fn default() -> Self {
+        Self {
+            query_type: Default::default(),
+            default_queue_count: 15,
+        }
+    }
+}
+
 impl Default for LktConfig {
     fn default() -> Self {
         Self {
             host: Default::default(),
-            admin: Some(Default::default()),
             search: Default::default(),
+            password: Some("hashire".to_string()),
         }
     }
 }
@@ -48,15 +52,6 @@ impl Default for LktHostPort {
     }
 }
 
-impl Default for LktAdminConfig {
-    fn default() -> Self {
-        Self {
-            user: Some("sakura".to_string()),
-            password: Some("sakura".to_string()),
-        }
-    }
-}
-
 impl Default for LktHostConfig {
     fn default() -> Self {
         Self {
@@ -67,30 +62,23 @@ impl Default for LktHostConfig {
 }
 
 impl LktConfig {
-    pub fn has_valid_admin_config(&self) -> bool {
-        matches!(&self.admin, Some(LktAdminConfig {
-                user: Some(user),
-                password: Some(password),
-            }) if !user.trim().is_empty() && !password.trim().is_empty())
-    }
-}
-
-pub fn get_or_write_default() -> Result<LktConfig, String> {
-    let path = commons::user_config_directory("amadeus").join("lkt.toml");
-    match std::fs::read_to_string(&path) {
-        Ok(config) => toml::from_str(&config).map_err(|err| {
-            let path = path.to_string_lossy();
-            format!("invalid config file `{path}`: {err}")
-        }),
-        Err(_) => {
-            let default_config = LktConfig::default();
-            let pretty_config = toml::to_string_pretty(&default_config)
-                .expect("failed to prettify the default config...");
-            std::fs::write(&path, pretty_config).map_err(|err| {
+    pub fn get_or_write_default() -> Result<LktConfig, String> {
+        let path = commons::user_config_directory("amadeus").join("lkt.toml");
+        match std::fs::read_to_string(&path) {
+            Ok(config) => toml::from_str(&config).map_err(|err| {
                 let path = path.to_string_lossy();
-                format!("failed to write default config to file `{path}`: {err}")
-            })?;
-            Ok(default_config)
+                format!("invalid config file `{path}`: {err}")
+            }),
+            Err(_) => {
+                let default_config = LktConfig::default();
+                let pretty_config = toml::to_string_pretty(&default_config)
+                    .expect("failed to prettify the default config...");
+                std::fs::write(&path, pretty_config).map_err(|err| {
+                    let path = path.to_string_lossy();
+                    format!("failed to write default config to file `{path}`: {err}")
+                })?;
+                Ok(default_config)
+            }
         }
     }
 }
diff --git a/src/rust/amadeus-next/lkt-rs/src/main.rs b/src/rust/amadeus-next/lkt-rs/src/main.rs
index 38230133..9bfb3a7c 100644
--- a/src/rust/amadeus-next/lkt-rs/src/main.rs
+++ b/src/rust/amadeus-next/lkt-rs/src/main.rs
@@ -3,18 +3,20 @@ mod config;
 mod parsers;
 mod types;
 
+use crate::{args::*, config::LktConfig};
+use amalib::{LektorQuery, LektorResponse};
+
 #[tokio::main(worker_threads = 2)]
 async fn main() {
     commons::Report::install_debug_hook::<std::backtrace::Backtrace>(|_, _| {});
     commons::logger::init(Some(commons::log::Level::Trace))
         .unwrap_or_else(|e| panic!("failed to install logger: {e}"));
-    let config = config::get_or_write_default().expect("failed to get or write default config");
-
-    let cmd = args::LktCommand::parse(&config).expect("oupsy");
+    let config = LktConfig::get_or_write_default().expect("failed to get or write default config");
+    let cmd = LktCommand::parse(&config).expect("oupsy");
     handle_cmd(config, cmd).await
 }
 
-async fn handle_cmd(config: config::LktConfig, cmd: args::LktCommand) {
+async fn handle_cmd(config: LktConfig, cmd: LktCommand) {
     commons::log::debug!("{config:#?}\ncmd = {cmd:#?}");
     let conn = match config.host.socket {
         config::LktHostPort::UNIX => unimplemented!("connexion to unix socket is not implemented"),
@@ -23,10 +25,10 @@ async fn handle_cmd(config: config::LktConfig, cmd: args::LktCommand) {
             .expect("failed to connect to the lektord server"),
     };
     match cmd {
-        args::LktCommand::Queue(cmd) => handle_cmd_queue(config, conn, cmd).await,
-        args::LktCommand::Search(cmd) => handle_cmd_search(config, conn, cmd).await,
-        args::LktCommand::Playlist(cmd) => handle_cmd_playlist(config, conn, cmd).await,
-        args::LktCommand::Admin(cmd) => handle_cmd_admin(config, conn, cmd).await,
+        LktCommand::Queue(cmd) => handle_cmd_queue(config, conn, cmd).await,
+        LktCommand::Search(cmd) => handle_cmd_search(config, conn, cmd).await,
+        LktCommand::Playlist(cmd) => handle_cmd_playlist(config, conn, cmd).await,
+        LktCommand::Admin(cmd) => handle_cmd_admin(config, conn, cmd).await,
     }
 }
 
@@ -43,49 +45,66 @@ macro_rules! send {
     }};
 }
 
-async fn handle_cmd_queue(
-    _: config::LktConfig,
-    _: amalib::LektorConnexion,
-    _: args::LktQueueCommand,
-) {
-    unimplemented!()
+async fn handle_cmd_queue(_: LktConfig, mut conn: amalib::LektorConnexion, cmd: LktQueueCommand) {
+    match cmd {
+        LktQueueCommand::ShowCurrent => {
+            send!(conn => LektorQuery::CurrentKara; LektorResponse::CurrentKara(kara) => {
+                kara.maybe_into_inner().map(|kara| println!("{kara:#?}"));
+            })
+        }
+        LktQueueCommand::ShowStatus => {
+            send!(conn => LektorQuery::PlaybackStatus; LektorResponse::PlaybackStatus(status) => {
+                println!("{status:#?}");
+            })
+        }
+        LktQueueCommand::List { range } => todo!(),
+        LktQueueCommand::Next => todo!(),
+        LktQueueCommand::Previous => todo!(),
+        LktQueueCommand::Pause => todo!(),
+        LktQueueCommand::UnPause => todo!(),
+        LktQueueCommand::TogglePause => todo!(),
+        LktQueueCommand::Stop => todo!(),
+        LktQueueCommand::Play { index } => todo!(),
+        LktQueueCommand::Add { priority, query } => todo!(),
+        LktQueueCommand::Crop => todo!(),
+        LktQueueCommand::Clear { up_to_lvl } => todo!(),
+        LktQueueCommand::SwapPositions { p1, p2 } => todo!(),
+        LktQueueCommand::SeekIdInQueue { id } => todo!(),
+        LktQueueCommand::Shuffle { up_to_lvl } => todo!(),
+    }
 }
 
-async fn handle_cmd_search(
-    _: config::LktConfig,
-    _: amalib::LektorConnexion,
-    _: args::LktSearchCommand,
-) {
+async fn handle_cmd_search(_: LktConfig, _: amalib::LektorConnexion, _: LktSearchCommand) {
     unimplemented!()
 }
 
 async fn handle_cmd_playlist(
-    _: config::LktConfig,
+    _: LktConfig,
     mut conn: amalib::LektorConnexion,
-    cmd: args::LktPlaylistCommand,
+    cmd: LktPlaylistCommand,
 ) {
     match cmd {
-        args::LktPlaylistCommand::Create { name } => {
-            send!(conn => amalib::LektorQuery::CreatePlaylist(name); ok)
+        LktPlaylistCommand::Create { name } => {
+            send!(conn => LektorQuery::CreatePlaylist(name); ok)
         }
-        args::LktPlaylistCommand::Destroy { name } => {
-            send!(conn => amalib::LektorQuery::DestroyPlaylist(name); ok)
+        LktPlaylistCommand::Destroy { name } => {
+            send!(conn => LektorQuery::DestroyPlaylist(name); ok)
         }
-        args::LktPlaylistCommand::Add { name, query } => {
-            send!(conn => amalib::LektorQuery::AddToPlaylist(name, query); ok)
+        LktPlaylistCommand::Add { name, query } => {
+            send!(conn => LektorQuery::AddToPlaylist(name, query); ok)
         }
-        args::LktPlaylistCommand::Remove { name, query } => {
-            send!(conn => amalib::LektorQuery::RemoveFromPlaylist(name, query); ok)
+        LktPlaylistCommand::Remove { name, query } => {
+            send!(conn => LektorQuery::RemoveFromPlaylist(name, query); ok)
         }
-        args::LktPlaylistCommand::List => {
-            send!(conn => amalib::LektorQuery::ListAllPlaylists
-                ; amalib::LektorResponse::PlaylistSet(playlists) => {
+        LktPlaylistCommand::List => {
+            send!(conn => LektorQuery::ListAllPlaylists
+                ; LektorResponse::PlaylistSet(playlists) => {
                     playlists.into_iter().for_each(|plt| println!("{plt}"))
             })
         }
-        args::LktPlaylistCommand::ListContent { name } => {
-            send!(conn => amalib::LektorQuery::ListPlaylist(name)
-                ; amalib::LektorResponse::KaraSet(karas) => {
+        LktPlaylistCommand::ListContent { name } => {
+            send!(conn => LektorQuery::ListPlaylist(name)
+                ; LektorResponse::KaraSet(karas) => {
                     karas.into_iter().for_each(|kara| println!("{kara}"))
             })
         }
@@ -93,13 +112,23 @@ async fn handle_cmd_playlist(
 }
 
 async fn handle_cmd_admin(
-    config: config::LktConfig,
-    _: amalib::LektorConnexion,
-    cmd: args::LktAdminCommand,
+    config: LktConfig,
+    mut conn: amalib::LektorConnexion,
+    cmd: LktAdminCommand,
 ) {
-    if config.has_valid_admin_config() {
-        unimplemented!("handle admin command: {cmd:#?}")
-    } else {
-        panic!("no admin config block in config file")
+    match config.password {
+        Some(password) => {
+            let cmd = Box::new(match cmd {
+                LktAdminCommand::Ping => LektorQuery::Ping,
+                LktAdminCommand::Kill => LektorQuery::KillServer,
+                LktAdminCommand::Restart => LektorQuery::RestartServer,
+                LktAdminCommand::Update { dry: true } => LektorQuery::Update,
+                LktAdminCommand::Update { dry: false } => LektorQuery::DryUpdate,
+                LktAdminCommand::Populate { dry: false } => LektorQuery::Populate,
+                LktAdminCommand::Populate { dry: true } => LektorQuery::DryPopulate,
+            });
+            send!(conn => LektorQuery::ConnectAsUser(password, cmd); ok);
+        }
+        None => panic!("no admin config block in config file"),
     }
 }
-- 
GitLab