Working connection and display of info

This commit is contained in:
Franz Dietrich 2024-01-19 21:12:52 +01:00
parent a7b3ecf178
commit a667c6d98b
18 changed files with 264 additions and 1565 deletions

3
.cargo/config.toml Normal file
View File

@ -0,0 +1,3 @@
[build]
rustflags = ["--cfg", "tokio_unstable"]

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target /target
/vendor
/Cargo.lock
.vscode/launch.json

1379
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,16 @@ pki-types = { package = "rustls-pki-types", version = "1.0", features = [
tokio-rustls = { version = "0.25" } tokio-rustls = { version = "0.25" }
env_logger = "0.10.1" env_logger = "0.10.1"
log = "0.4.20" log = "0.4.20"
tokio = { version = "1.34.0", features = ["full"] } tokio = { version = "1.34.0", features = ["full", "tracing"] }
webpki-roots = "0.26.0" webpki-roots = "0.26.0"
nom = "7.1.3" nom = "7.1.3"
managesieve = { path = "../managesieve" } managesieve = { path = "../managesieve" }
anyhow = "1.0" anyhow = "1.0"
thiserror = "1.0" thiserror = "1.0"
gtk4 = { version = "0.7.3", features = ["gnome_45"] } gtk4 = { version = "0.7", features = ["gnome_45", "blueprint"] }
serde = { version = "1.0.193", features = ["derive"] } serde = { version = "1.0.193", features = ["derive"] }
libadwaita = { version = "0.5.3", features = ["gtk_v4_10", "v1_4"] }
gtk-blueprint = "0.2"
phf = "0.11"
tracing = "0.1"
console-subscriber = "0.2"

3
build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=**/*.blp");
}

View File

@ -1,36 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<cambalache-project version="0.13.1" target_tk="gtk-4.0">
<ui>
(1,None,"sieverman.ui","sieverman.ui",None,None,"Franz Dietrich",None,None,None,None)
</ui>
<object>
(1,1,"GtkApplicationWindow","sieverman",None,None,None,None,-1,None),
(1,2,"GtkBox",None,1,None,None,None,-1,None),
(1,3,"GtkBox",None,2,None,None,None,1,None),
(1,4,"GtkActionBar",None,2,None,None,None,None,None),
(1,5,"GtkStatusbar",None,2,None,None,None,2,None),
(1,6,"GtkSpinner",None,3,None,None,None,1,None),
(1,7,"GtkLabel","messages",3,None,None,None,None,None),
(1,9,"GtkFrame",None,3,None,None,None,2,None),
(1,10,"GtkLabel","serverstatus_2",9,None,None,None,None,None)
</object>
<object_property>
(1,2,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(1,3,"GtkBox","baseline-child","0",None,None,None,None,None,None,None,None,None),
(1,3,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(1,6,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(1,7,"GtkLabel","label","Loading",None,None,None,None,None,None,None,None,None),
(1,10,"GtkLabel","label","Loading…",None,None,None,None,None,None,None,None,None)
</object_property>
<object_data>
(1,3,"GtkWidget",1,1,None,None,None,None,None,None),
(1,3,"GtkWidget",2,2,None,1,None,None,None,None),
(1,3,"GtkWidget",2,3,None,1,None,None,None,None),
(1,3,"GtkWidget",2,4,None,1,None,None,None,None),
(1,3,"GtkWidget",2,5,None,1,None,None,None,None),
(1,3,"GtkWidget",2,6,None,1,None,None,None,None),
(1,3,"GtkWidget",2,7,None,1,None,None,None,None),
(1,3,"GtkWidget",2,8,None,1,None,None,None,None)
</object_data>
</cambalache-project>

45
gui/main_window.blp Normal file
View File

@ -0,0 +1,45 @@
using Gtk 4.0;
using Adw 1;
Adw.ApplicationWindow window {
default-width: 900;
default-height: 500;
content: Gtk.Box {
orientation: vertical;
Adw.HeaderBar{
title-widget: Adw.WindowTitle {
title: "Sieverman";
};
Gtk.Button { label: "Neu";}
}
Gtk.Box {
orientation: horizontal;
vexpand: true;
halign: fill;
Gtk.Label{
label: "Log information";
hexpand: true;
}
Gtk.ScrolledWindow server_info {
width-request: 100;
Adw.PreferencesPage {
Adw.PreferencesGroup server_settings{
vexpand: true;
valign: center;
title: "Server Information";
description: "The information the server published on connection";
}}
}
}
Gtk.Statusbar{Gtk.Label{
label: "Status";
}}
};
}

12
gui/server_info_row.blp Normal file
View File

@ -0,0 +1,12 @@
using Gtk 4.0;
using Adw 1;
Adw.ActionRow server_info_row {
title: "Server2";
Gtk.Label server_info_row_value{
label: "Stalwart";
}}

View File

@ -1,41 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.16.0 -->
<interface>
<!-- interface-name sieverman.ui -->
<!-- interface-authors Franz Dietrich -->
<requires lib="gtk" version="4.12"/>
<object class="GtkApplicationWindow" id="sieverman">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkActionBar"/>
</child>
<child>
<object class="GtkBox">
<property name="baseline-child">0</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="messages">
<property name="label">Loading</property>
</object>
</child>
<child>
<object class="GtkSpinner">
<property name="spinning">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="serverstatus">
<property name="label">Loading…</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkStatusbar"/>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,36 +1,29 @@
use log::{info, trace};
use sieverman::ConnectionInfo; use sieverman::ConnectionInfo;
use tracing::info;
use crate::protocol::BackToFront; use crate::protocol::BackToFront;
pub(crate) async fn run( pub(crate) async fn run(
to_frontend_tx: tokio::sync::mpsc::Sender<BackToFront>, to_frontend_tx: tokio::sync::mpsc::Sender<BackToFront>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
trace!("Starting up sieverman…"); info!("Starting up sieverman…");
info!("Creating connection info");
let info = ConnectionInfo::new("teilgedanken.de", 4190); let info = ConnectionInfo::new("teilgedanken.de", 4190);
let mut connected = info.connect().await.unwrap(); info!("connecting…");
info!("connected to the server"); let future_info = info.connect();
loop { info!("waiting for the connection to be established");
match connected.read_introduction().await { let mut connected = match future_info.await {
Ok(sieverman::IsComplete::Yes(c)) => { Ok(v) => v,
connected = c; Err(e) => {
break; info!("Failed to connect: {:?}", e);
} panic!("Something went wrong");
Ok(sieverman::IsComplete::No(c)) => { }
trace!("Incomplete data waiting for some time…"); };
connected = c
} info!("Fully read the introduction:");
Err(e) => return Err(anyhow::anyhow!("something went wrong: {}", e)),
};
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
connected.log_buffer().await;
}
trace!("Fully read the introduction:");
connected.log_buffer().await;
connected.log_server_settings().await;
to_frontend_tx to_frontend_tx
.send(BackToFront::ServerConnected( .send(BackToFront::ServerConnected(
connected.get_server_capabilities(), connected.get_server_capabilities().clone(),
)) ))
.await .await
.unwrap(); .unwrap();

View File

@ -1,11 +1,14 @@
use std::{cell::RefCell, fmt::Display, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::__COMPILED_BLUEPRINT_MAP__;
use gtk4::{ use gtk4::{
glib, glib,
prelude::{ApplicationExt, BoxExt, GtkWindowExt, WidgetExt}, prelude::{ApplicationExt, GtkWindowExt},
Application, ApplicationWindow, Builder, Label, Application,
}; };
use log::trace; use gtk_blueprint::get_blp;
use libadwaita::prelude::{PreferencesGroupExt, PreferencesRowExt};
use tracing::trace;
use crate::protocol::BackToFront; use crate::protocol::BackToFront;
@ -25,50 +28,30 @@ fn build_ui(
app: &gtk4::Application, app: &gtk4::Application,
from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>, from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>,
) { ) {
let action_content_status = gtk4::Box::builder() let builder = gtk4::Builder::new();
.orientation(gtk4::Orientation::Vertical) builder
.spacing(5) .add_from_string(get_blp!("gui/main_window.blp"))
.build(); .expect("Failed to parse blueprint");
let action = gtk4::ActionBar::new(); let main_window = builder
let status = gtk4::Statusbar::new(); .object::<libadwaita::ApplicationWindow>("window")
let main_log_server_info = gtk4::Box::builder() .unwrap();
.orientation(gtk4::Orientation::Horizontal) let prefrences_group = builder
.hexpand(true) .object::<libadwaita::PreferencesGroup>("server_settings")
.vexpand(true) .unwrap();
.halign(gtk4::Align::Fill) main_window.set_application(Some(app));
.build();
action_content_status.append(&action);
action_content_status.append(&main_log_server_info);
action_content_status.append(&status);
let log = gtk4::Label::builder().label("Log").hexpand(true).build();
let server_info_content = gtk4::Label::builder().label("Server Info").build();
let server_info_container = gtk4::ScrolledWindow::builder()
.child(&server_info_content)
.width_request(400)
.build();
main_log_server_info.append(&log);
main_log_server_info.append(&server_info_container);
let main_window = gtk4::ApplicationWindow::builder()
.application(app)
.title("Sieverman")
.height_request(400)
.width_request(400)
.child(&action_content_status)
.build();
main_window.present();
let server_info = Rc::new(RefCell::new(server_info_content));
let future = { let future = {
let mut data_event_receiver = from_backend_rx.take().expect("data_event_reciver"); let mut data_event_receiver = from_backend_rx.take().expect("data_event_reciver");
async move { async move {
while let Some(event) = data_event_receiver.recv().await { while let Some(event) = data_event_receiver.recv().await {
trace!("data event: {:?}", event);
match event { match event {
BackToFront::ServerConnected(message) => { BackToFront::ServerConnected(caps) => {
server_info prefrences_group.add(&row_in_settings("Greeting", &caps.implementation));
.borrow_mut() prefrences_group
.set_label(&format!("{:#?}", message)); .add(&row_in_settings("Authentication", &caps.sasl.join(",")));
prefrences_group
.add(&row_in_settings("Starttls", &caps.starttls.to_string()));
prefrences_group.add(&row_in_settings("Version", &caps.version));
} }
} }
} }
@ -78,4 +61,22 @@ fn build_ui(
let c = glib::MainContext::default(); let c = glib::MainContext::default();
c.spawn_local(future); c.spawn_local(future);
main_window.present(); main_window.present();
trace!("Window is visible");
}
fn row_in_settings(name: &str, value: &str) -> libadwaita::ActionRow {
let builder = gtk4::Builder::new();
builder
.add_from_string(get_blp!("gui/server_info_row.blp"))
.expect("Failed to parse blueprint");
let row = builder
.object::<libadwaita::ActionRow>("server_info_row")
.unwrap();
row.set_title(name);
let label = builder
.object::<gtk4::Label>("server_info_row_value")
.unwrap();
label.set_label(value);
row
} }

48
src/bin/old/bluep_test.rs Normal file
View File

@ -0,0 +1,48 @@
use gtk4::prelude::{ApplicationExt, ApplicationExtManual, GtkWindowExt};
// We're using gtk-builder feature here
use gtk_blueprint::get_blp;
use libadwaita::prelude::PreferencesGroupExt;
gtk_blueprint::gen_blp_map!("gui");
fn main() {
gtk4::init().expect("GTK initialization failed");
libadwaita::init().expect("Adwaita initialization failed");
// Create app
let application = gtk4::Application::builder()
.application_id("de.teilgedanken.sieverman")
.build();
// Init app window and show it
application.connect_activate(|app| {
// You also can parse blueprint with Parser::parse
// and then use it in gtk4::Builder
let builder = gtk4::Builder::new();
builder
.add_from_string(get_blp!("gui/main_window.blp"))
.expect("Failed to parse blueprint");
let window = builder
.object::<libadwaita::ApplicationWindow>("window")
.unwrap();
let settings: libadwaita::PreferencesGroup = builder
.object("server_settings")
.expect("get server settings");
for _ in 0..5 {
let builder = gtk4::Builder::new();
builder
.add_from_string(get_blp!("gui/headerbar.blp"))
.expect("Failed to parse blueprint");
let widget: libadwaita::PreferencesRow = builder
.object("server_info_row")
.expect("Couldn't get widget");
settings.add(&widget);
}
window.set_application(Some(app));
window.present();
});
// Run app
application.run();
}

View File

@ -1,4 +1,3 @@
use log::info;
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::character::is_space; use nom::character::is_space;
@ -12,6 +11,7 @@ use nom::{character, number};
use sieverman::parser::parse_server_config; use sieverman::parser::parse_server_config;
use sieverman::{Methods, ServerSettings}; use sieverman::{Methods, ServerSettings};
use std::error::Error; use std::error::Error;
use tracing::info;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
env_logger::init(); env_logger::init();

View File

@ -9,7 +9,6 @@
/// that is sensible outside of example code. /// that is sensible outside of example code.
use std::sync::Arc; use std::sync::Arc;
use log::{error, info, trace};
use managesieve::{Capability, MSResult, Response}; use managesieve::{Capability, MSResult, Response};
use nom::{error::ParseError, IResult}; use nom::{error::ParseError, IResult};
use sieverman::parser::parse_server_config; use sieverman::parser::parse_server_config;
@ -23,6 +22,7 @@ use tokio_rustls::{
rustls::{ClientConfig, KeyLogFile, RootCertStore}, rustls::{ClientConfig, KeyLogFile, RootCertStore},
TlsConnector, TlsConnector,
}; };
use tracing::{error, info, trace};
#[macro_export] #[macro_export]
macro_rules! parse_response { macro_rules! parse_response {

View File

@ -1,7 +1,7 @@
use managesieve::Capability; use managesieve::Capabilities;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub enum BackToFront { pub enum BackToFront {
ServerConnected(Vec<Capability>), ServerConnected(Capabilities),
} }

View File

@ -1,15 +1,27 @@
mod backend; mod backend;
mod gui; mod gui;
pub mod protocol; pub mod protocol;
use std::{cell::RefCell, rc::Rc, thread}; use std::{cell::RefCell, rc::Rc, thread, time::Duration};
use gtk4::{glib, prelude::ApplicationExtManual as _}; use gtk4::{glib, prelude::ApplicationExtManual as _};
use tracing::{info, trace};
gtk_blueprint::gen_blp_map!("gui");
fn main() -> glib::ExitCode { fn main() -> glib::ExitCode {
env_logger::init(); console_subscriber::ConsoleLayer::builder()
// set how long the console will retain data from completed tasks
.retention(Duration::from_secs(60))
// set the address the server is bound to
.server_addr(([127, 0, 0, 1], 6669))
.init();
gtk4::init().expect("Failed to initialize GTK"); gtk4::init().expect("Failed to initialize GTK");
libadwaita::init().expect("Adwaita initialization failed");
let (to_frontent_tx, from_backend_rx) = tokio::sync::mpsc::channel(5); let (to_frontent_tx, from_backend_rx) = tokio::sync::mpsc::channel(5);
let _handle = thread::spawn(move || { let _handle = thread::spawn(move || {
info!("Running Backend");
tokio::runtime::Builder::new_current_thread() tokio::runtime::Builder::new_current_thread()
.enable_io() .enable_io()
.enable_time() .enable_time()
@ -21,5 +33,8 @@ fn main() -> glib::ExitCode {
let app = gui::main_window::get_app(Rc::new(RefCell::new(Some(from_backend_rx)))); let app = gui::main_window::get_app(Rc::new(RefCell::new(Some(from_backend_rx))));
app.run() let res = app.run();
trace!("End");
res
} }

View File

@ -1,20 +1,18 @@
use std::{any, net::SocketAddr, sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use log::{error, info, trace}; use managesieve::Capabilities;
use managesieve::Capability;
use std::net::ToSocketAddrs;
use thiserror::Error; use thiserror::Error;
use tokio::{ use tokio::{
io::{split, AsyncBufReadExt, BufReader, ReadHalf, WriteHalf}, io::{split, AsyncBufReadExt, BufReader, WriteHalf},
net::TcpStream, net::TcpStream,
sync::Mutex, sync::Mutex,
task::JoinHandle,
}; };
use tokio_rustls::{ use tokio_rustls::{
client::TlsStream, client::TlsStream,
rustls::{ClientConfig, KeyLogFile, RootCertStore}, rustls::{ClientConfig, KeyLogFile, RootCertStore},
TlsConnector, TlsConnector,
}; };
use tracing::{error, info, trace};
pub mod parser; pub mod parser;
@ -32,6 +30,7 @@ impl ConnectionInfo {
} }
} }
pub async fn connect(self) -> Result<ConnectionConnected, anyhow::Error> { pub async fn connect(self) -> Result<ConnectionConnected, anyhow::Error> {
info!("connecting to the server");
trace!("Building Cert Store"); trace!("Building Cert Store");
let mut root_store = RootCertStore::empty(); let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
@ -48,11 +47,11 @@ impl ConnectionInfo {
let domain = pki_types::ServerName::try_from(self.domain.clone())?.to_owned(); let domain = pki_types::ServerName::try_from(self.domain.clone())?.to_owned();
trace!("Attach the connector"); trace!("Attach the connector");
let stream = connector.connect(domain, stream).await?; let stream = connector.connect(domain, stream).await?;
let (reader, mut writer) = split(stream); let (reader, writer) = split(stream);
let buffer = Arc::new(Mutex::new(String::new())); let buffer = Arc::new(Mutex::new(String::new()));
let spawned_buffer = buffer.clone(); let spawned_buffer = buffer.clone();
let join_handle = tokio::spawn(async move { let _join_handle = tokio::spawn(async move {
let mut buf = String::new(); let mut buf = String::new();
let mut reader = BufReader::new(reader); let mut reader = BufReader::new(reader);
loop { loop {
@ -67,13 +66,36 @@ impl ConnectionInfo {
} }
buf.clear(); buf.clear();
}; };
tokio::time::sleep(Duration::from_millis(50)).await;
} }
}); });
let caps = loop {
let mut buf = buffer.lock().await;
trace!("reading new input:\n{}", buf);
match managesieve::response_capability(&buf.to_string()) {
Ok((rest, caps, resp)) => match resp.tag {
managesieve::OkNoBye::Ok => {
buf.clear();
buf.push_str(rest);
info!("Read the introduction");
break caps;
}
managesieve::OkNoBye::No | managesieve::OkNoBye::Bye => {
trace!("Connection closed!");
//panic!("Invalid Response")
}
},
Err(e) => trace!("(temporary) error: {}", e),
}
tokio::time::sleep(Duration::from_millis(50)).await;
};
trace!("Capabilities read: {:#?}", &caps);
Ok(ConnectionConnected { Ok(ConnectionConnected {
info: self, info: self,
writer: Arc::new(writer), //server_settings: todo!(), writer: Arc::new(writer),
buffer, buffer,
server_settings: Vec::new(), //join_handle: Arc::new(join_handle), server_settings: caps,
}) })
} }
} }
@ -85,7 +107,7 @@ pub struct ConnectionConnected {
writer: Arc<WriteHalf<TlsStream<TcpStream>>>, writer: Arc<WriteHalf<TlsStream<TcpStream>>>,
buffer: Arc<Mutex<String>>, buffer: Arc<Mutex<String>>,
//pub join_handle: Arc<JoinHandle<()>>, //pub join_handle: Arc<JoinHandle<()>>,
server_settings: Vec<Capability>, server_settings: Capabilities,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -95,7 +117,7 @@ pub enum ReadError {
#[error("The data the server responded was invalid")] #[error("The data the server responded was invalid")]
InvalidResponse(#[source] managesieve::Error), InvalidResponse(#[source] managesieve::Error),
} }
#[derive(Debug)]
pub enum IsComplete<T> { pub enum IsComplete<T> {
Yes(T), Yes(T),
No(T), No(T),
@ -103,11 +125,12 @@ pub enum IsComplete<T> {
impl ConnectionConnected { impl ConnectionConnected {
pub async fn log_buffer(&self) { pub async fn log_buffer(&self) {
trace!("Buffer is: \n{}", self.buffer.lock().await) info!("Buffer is: \n{}", self.buffer.lock().await)
} }
pub async fn log_server_settings(&self) { pub async fn log_server_settings(&self) {
trace!("Serversettings:\n{:?}", self.server_settings) info!("Serversettings:\n{:?}", self.server_settings)
} }
#[tracing::instrument(name = "gettin the intro")]
pub async fn read_introduction(self) -> Result<IsComplete<Self>, anyhow::Error> { pub async fn read_introduction(self) -> Result<IsComplete<Self>, anyhow::Error> {
let Self { let Self {
info, info,
@ -116,12 +139,11 @@ impl ConnectionConnected {
buffer, buffer,
server_settings, server_settings,
} = self; } = self;
tokio::time::sleep(Duration::from_millis(500)).await; tokio::time::sleep(Duration::from_millis(50)).await;
let mut bf = buffer.lock().await; let mut bf = buffer.lock().await;
let bf_str = bf.clone(); let response = match managesieve::response_capability(&bf.to_string()) {
match managesieve::response_capability(&bf_str) {
Ok((rest, capability, response)) => { Ok((rest, capability, response)) => {
trace!("{:?}", response); info!("Successfully read the introduction {:?}", response);
bf.clear(); bf.clear();
bf.push_str(rest); bf.push_str(rest);
drop(bf); drop(bf);
@ -132,12 +154,15 @@ impl ConnectionConnected {
server_settings: capability, server_settings: capability,
})) }))
} }
Err(managesieve::Error::IncompleteResponse) => Ok(IsComplete::No(Self { Err(managesieve::Error::IncompleteResponse) => {
info, trace!("incomplete introduction");
writer, Ok(IsComplete::No(Self {
buffer: buffer.clone(), info,
server_settings, writer,
})), buffer: buffer.clone(),
server_settings,
}))
}
Err(managesieve::Error::InvalidResponse) => { Err(managesieve::Error::InvalidResponse) => {
error!("invalid response"); error!("invalid response");
Err(managesieve::Error::InvalidResponse)? Err(managesieve::Error::InvalidResponse)?
@ -146,18 +171,20 @@ impl ConnectionConnected {
error!("invalid input"); error!("invalid input");
Err(managesieve::Error::InvalidResponse)? Err(managesieve::Error::InvalidResponse)?
} }
} Err(managesieve::Error::MissingLine(line)) => {
} error!("a capability line was missing: {:?}", line);
pub fn get_greeting(&self) -> Option<String> { Err(managesieve::Error::InvalidResponse)?
for c in self.server_settings.iter() {
if let Capability::Implementation(s) = c {
return Some(s.clone());
} }
} };
None
response
} }
pub fn get_server_capabilities(&self) -> Vec<Capability> {
self.server_settings.clone() pub fn get_greeting(&self) -> String {
self.server_settings.implementation.to_string()
}
pub fn get_server_capabilities(&self) -> &Capabilities {
&self.server_settings
} }
} }

View File

@ -1,4 +1,3 @@
use log::{info, trace};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::is_not, bytes::complete::is_not,
@ -11,6 +10,7 @@ use nom::{
sequence::preceded, sequence::preceded,
IResult, IResult,
}; };
use tracing::trace;
use crate::{parser::utils::key_value, Methods, ServerSettings}; use crate::{parser::utils::key_value, Methods, ServerSettings};