Numerous new commands, documentation changews, started with ssl

This commit is contained in:
famfo 2021-11-08 17:56:06 +01:00 committed by Yash Karandikar
parent 162d5a42d0
commit 92ee775225
4 changed files with 438 additions and 107 deletions

102
Cargo.lock generated
View file

@ -2,15 +2,111 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "circe"
version = "0.1.2"
dependencies = [
"openssl",
"openssl-sys",
"serde",
"serde_derive",
"toml",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "libc"
version = "0.2.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "openssl"
version = "0.10.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-sys",
]
[[package]]
name = "openssl-src"
version = "300.0.2+3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14a760a11390b1a5daf72074d4f6ff1a6e772534ae191f999f57e9ee8146d1fb"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf"
dependencies = [
"autocfg",
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
[[package]]
name = "pkg-config"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
[[package]]
name = "proc-macro2"
version = "1.0.32"
@ -71,3 +167,9 @@ name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"

View file

@ -11,6 +11,8 @@ exclude = ["src/main.rs", "config.toml"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
toml = "0.5.8"
serde_derive = "1.0.130"
serde = "1.0.130"
toml = "0.5"
serde_derive = "1.0"
serde = "1.0"
openssl = { version = "0.10", features = ["vendored"] }
openssl-sys = "0.9"

View file

@ -17,11 +17,11 @@
//! }
//! # break;
//! }
//!
//! # Ok(())
//! }
#![warn(missing_docs)]
use openssl::ssl::{SslConnector, SslMethod};
use std::borrow::Cow;
use std::fs::File;
use std::io::{Error, Read, Write};
@ -33,7 +33,8 @@ use serde_derive::Deserialize;
/// An IRC client
pub struct Client {
config: Config,
stream: TcpStream,
stream: Option<TcpStream>,
sslstream: Option<openssl::ssl::SslStream<TcpStream>>,
}
/// Config for the IRC client
@ -44,6 +45,7 @@ pub struct Config {
mode: Option<String>,
nickname: Option<String>,
port: u16,
ssl: bool,
username: String,
}
@ -55,27 +57,78 @@ pub enum CapMode {
}
/// IRC commands
/// Not reccomended to use, use the helper functions instead
#[derive(Debug)]
pub enum Command {
// TODO:
// SERVICE <nickname> <reserved> <distribution> <type> <reserved> <info>
// SQUIT <server> <comment>
//
/// Gets information about the admin of the IRC server.
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.admin("192.168.178.100")?;
/// # Ok::<(), std::io::Error>(())
/// ```
ADMIN(
/// Target
String,
),
/// Sets the user status to AWAY
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.away("AFK")?;
/// # Ok::<(), std::io::Error>(())
/// ```
AWAY(
/// Message
String,
),
#[doc(hidden)]
CAP(CapMode),
/// Invite user to channel
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.invite("liblirc", "#circe")?;
/// # Ok::<(), std::io::Error>(())
/// ```
INVITE(
/// User
String,
/// Channel
String,
),
/// Joins a channel
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::JOIN("#main".to_string()))?;
/// client.join("#main")?;
/// # Ok::<(), std::io::Error>(())
/// ```
JOIN(
/// Channel
String,
),
/// Lists all channels and their topics
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.list(None, None)?;
/// # Ok::<(), std::io::Error>(())
/// ```
LIST(
/// Channel
Option<String>,
// Server
Option<String>,
),
/// Sets the mode of the user
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::MODE("#main".to_string(), Some("+B".to_string())))?;
/// client.mode("test", Some("+B"))?;
/// # Ok::<(), std::io::Error>(())
/// ```
/// If the MODE is not given (e.g. None), then the client will send "MODE target"
@ -85,18 +138,49 @@ pub enum Command {
/// Mode
Option<String>,
),
/// List all nicknames visiable to the Client
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.names("#main,#circe", None)?;
/// # Ok::<(), std::io::Error>(())
/// ```
NAMES(
/// Channel
String,
/// Server to foreward request to
Option<String>,
),
#[doc(hidden)]
NICK(String),
/// Attempts to identify as a channel operator
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.oper("circe", "foo")?;
/// # Ok::<(), std::io::Error>(())
/// ```
OPER(
/// Username
String,
/// Password
String,
),
/// Everything that is not a command
OTHER(String),
/// Leaves channels
/// Leave a channel
/// ```no_run
/// # use circe::*
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_part(Command::PART("#main".to_string()))?;
/// client.part("#main")?;
/// # Ok::<(), std::io::Error>(())
/// ```
PART(String),
PART(
/// Target
String,
),
#[doc(hidden)]
PASS(String),
#[doc(hidden)]
PING(String),
#[doc(hidden)]
@ -105,7 +189,7 @@ pub enum Command {
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::PRIVMSG("#main".to_string(), "This is an example message".to_string()))?;
/// client.privmsg("This is an example message")?;
/// # Ok::<(), std::io::Error>(())
/// ```
PRIVMSG(
@ -118,10 +202,26 @@ pub enum Command {
/// ```no_run
/// # use circe::*
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::QUIT("Leaving...".to_string()))?;
/// client.quit(Some("Leaving..."))?;
/// # Ok::<(), std::io::Error>(())
/// ```
QUIT(String),
QUIT(
/// Leave message
String,
),
/// Sets or gets the topic of a channel
/// ```no_run
/// # use circe::*
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.topic("#main", Some("main channel"))?;
/// # Ok::<(), std::io::Error>(())
/// ```
TOPIC(
/// Channel
String,
/// Topic
Option<String>,
),
#[doc(hidden)]
USER(String, String, String, String),
}
@ -135,9 +235,9 @@ impl Command {
return Self::PONG(command);
} else if new.contains("PRIVMSG") {
let parts: Vec<&str> = new.split_whitespace().collect();
let target = parts[2];
let mut builder = String::new();
for part in parts[3..].to_vec() {
builder.push_str(&format!("{} ", part));
}
@ -150,7 +250,7 @@ impl Command {
}
impl Client {
/// Creates a new client with a given config
/// Creates a new client with a given [`Config`].
/// ```no_run
/// # use circe::*;
/// # let config = Config::from_toml("config.toml")?;
@ -158,21 +258,36 @@ impl Client {
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// Errors if the client could not connect to the given host.
/// Returns error if the client could not connect to the host.
pub fn new(config: Config) -> Result<Self, Error> {
let stream = TcpStream::connect(format!("{}:{}", config.host, config.port))?;
Ok(Self { stream, config })
let sslstream: openssl::ssl::SslStream<TcpStream>;
if config.ssl {
let connector = SslConnector::builder(SslMethod::tls())?.build();
sslstream = connector.connect(config.host.as_str(), stream).unwrap();
return Ok(Self {
config,
stream: None,
sslstream: Some(sslstream),
});
} else {
return Ok(Self {
config,
stream: Some(stream),
sslstream: None,
});
}
}
/// Identify user and join the specified channels
/// Identify user and joins the in the [`Config`] specified channels.
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.identify()?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// Errors if the client could not write to the stream.
/// Returns error if the client could not write to the stream.
pub fn identify(&mut self) -> Result<(), Error> {
self.write_command(Command::CAP(CapMode::END))?;
self.write_command(Command::USER(
@ -214,15 +329,22 @@ impl Client {
fn read_string(&mut self) -> Option<String> {
let mut buffer = [0u8; 512];
match self.stream.read(&mut buffer) {
Ok(_) => {}
Err(_) => return None,
};
if self.config.ssl {
match self.sslstream.unwrap().read(&mut buffer) {
Ok(_) => {}
Err(_) => return None,
};
} else {
match self.stream.as_ref().unwrap().read(&mut buffer) {
Ok(_) => {}
Err(_) => return None,
};
}
Some(String::from_utf8_lossy(&buffer).into())
}
/// Read data coming from the IRC as a [`Command`]
/// Read data coming from the IRC as a [`Command`].
/// ```no_run
/// # use circe::*;
/// # fn main() -> Result<(), std::io::Error> {
@ -236,8 +358,7 @@ impl Client {
/// # Ok::<(), std::io::Error>(())
/// # }
/// ```
///
/// Errors if there are no new messages. This should not be taken as an actual Error, because nothing went wrong.
/// Returns error if there are no new messages. This should not be taken as an actual error, because nothing went wrong.
pub fn read(&mut self) -> Result<Command, ()> {
if let Some(string) = self.read_string() {
return Ok(Command::from_str(&string));
@ -249,26 +370,40 @@ impl Client {
fn write(&mut self, data: &str) -> Result<(), Error> {
let formatted = {
let new = format!("{}\r\n", data);
Cow::Owned(new) as Cow<str>
};
self.stream.write(formatted.as_bytes())?;
if self.config.ssl {
self.sslstream
.as_ref()
.unwrap()
.write(formatted.as_bytes())?;
} else {
self.stream.as_ref().unwrap().write(formatted.as_bytes())?;
}
Ok(())
}
/// Send a [`Command`] to the IRC
/// Send a [`Command`] to the IRC.<br>
/// Not reccomended to use, use the helper functions instead.
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// Errors if the stream could not write.
/// Returns error if the client could not write to the stream.
pub fn write_command(&mut self, command: Command) -> Result<(), Error> {
use Command::*;
let computed = match command {
ADMIN(target) => {
let formatted = format!("ADMIN {}", target);
Cow::Owned(formatted) as Cow<str>
}
AWAY(message) => {
let formatted = format!("AWAY {}", message);
Cow::Owned(formatted) as Cow<str>
}
CAP(mode) => {
use CapMode::*;
Cow::Borrowed(match mode {
@ -276,10 +411,34 @@ impl Client {
END => "CAP END",
}) as Cow<str>
}
INVITE(username, channel) => {
let formatted = format!("INVITE {} {}", username, channel);
Cow::Owned(formatted) as Cow<str>
}
JOIN(channel) => {
let formatted = format!("JOIN {}", channel);
Cow::Owned(formatted) as Cow<str>
}
LIST(channel, server) => {
let mut formatted = "LIST".to_string();
if let Some(channel) = channel {
formatted.push_str(format!(" {}", channel).as_str());
}
if let Some(server) = server {
formatted.push_str(format!(" {}", server).as_str());
}
Cow::Owned(formatted) as Cow<str>
}
NAMES(channel, server) => {
let formatted = {
if let Some(server) = server {
format!("NAMES {} {}", channel, server)
} else {
format!("NAMES {}", channel)
}
};
Cow::Owned(formatted) as Cow<str>
}
NICK(nickname) => {
let formatted = format!("NICK {}", nickname);
Cow::Owned(formatted) as Cow<str>
@ -292,7 +451,10 @@ impl Client {
format!("MODE {}", target)
}
};
Cow::Owned(formatted) as Cow<str>
}
OPER(nick, password) => {
let formatted = format!("OPER {} {}", nick, password);
Cow::Owned(formatted) as Cow<str>
}
OTHER(_) => {
@ -305,6 +467,10 @@ impl Client {
let formatted = format!("PART {}", target);
Cow::Owned(formatted) as Cow<str>
}
PASS(password) => {
let formatted = format!("PASS {}", password);
Cow::Owned(formatted) as Cow<str>
}
PING(target) => {
let formatted = format!("PING {}", target);
Cow::Owned(formatted) as Cow<str>
@ -318,7 +484,17 @@ impl Client {
Cow::Owned(formatted) as Cow<str>
}
QUIT(message) => {
let formatted = format!("QUIT {}", message);
let formatted = format!("QUIT :{}", message);
Cow::Owned(formatted) as Cow<str>
}
TOPIC(channel, topic) => {
let formatted = {
if let Some(topic) = topic {
format!("TOPIC {} :{}", channel, topic)
} else {
format!("TOPIC {}", channel)
}
};
Cow::Owned(formatted) as Cow<str>
}
USER(username, s1, s2, realname) => {
@ -331,70 +507,121 @@ impl Client {
Ok(())
}
// Helper functions
// Helper commands
/// Helper function for requesting information about the ADMIN of an IRC server
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.admin("192.168.178.100")?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn admin(&mut self, target: &str) -> Result<(), Error> {
self.write_command(Command::ADMIN(target.to_string()))?;
Ok(())
}
/// Helper function for setting the users status to AWAY
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.away("AFK")?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn away(&mut self, message: &str) -> Result<(), Error> {
self.write_command(Command::AWAY(message.to_string()))?;
Ok(())
}
/// Helper function for sending PRIVMSGs.
/// This makes
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.send_privmsg("#main", "Hello")?;
/// client.privmsg("#main", "Hello")?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// equivalent to
///
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn send_privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> {
pub fn privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> {
self.write_command(Command::PRIVMSG(channel.to_string(), message.to_string()))?;
Ok(())
}
/// Helper function to INVITE people to a channels
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.invite("liblirc", "#circe")?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn invite(&mut self, username: &str, channel: &str) -> Result<(), Error> {
self.write_command(Command::INVITE(username.to_string(), channel.to_string()))?;
Ok(())
}
/// Helper function for sending JOINs.
/// This makes
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.send_join("#main")?;
/// client.join("#main")?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// equivalent to
///
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::JOIN("#main".to_string()))?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn send_join(&mut self, channel: &str) -> Result<(), Error> {
pub fn join(&mut self, channel: &str) -> Result<(), Error> {
self.write_command(Command::JOIN(channel.to_string()))?;
Ok(())
}
/// Helper function for LISTing channels and modes
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.list(None, None)?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn list(&mut self, channel: Option<&str>, server: Option<&str>) -> Result<(), Error> {
let channel_config = {
if let Some(channel) = channel {
Some(channel.to_string())
} else {
None
}
};
let server_config = {
if let Some(server) = server {
Some(server.to_string())
} else {
None
}
};
self.write_command(Command::LIST(channel_config, server_config))?;
Ok(())
}
/// Helper function for getting all nicknames in a channel
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.names("#main,#circe", None)?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn names(&mut self, channel: &str, server: Option<&str>) -> Result<(), Error> {
if let Some(server) = server {
self.write_command(Command::NAMES(
channel.to_string(),
Some(server.to_string()),
))?;
} else {
self.write_command(Command::NAMES(channel.to_string(), None))?;
}
Ok(())
}
/// Helper function for sending MODEs.
/// This makes
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.send_mode("test", Some("+B"))?;
/// client.mode("test", Some("+B"))?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// equivalent to
///
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_command(Command::MODE("test".to_string(), Some("+B".to_string())))?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn send_mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> {
pub fn mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> {
if let Some(mode) = mode {
self.write_command(Command::MODE(target.to_string(), Some(mode.to_string())))?;
} else {
@ -404,54 +631,54 @@ impl Client {
}
/// Helper function for leaving channels.
/// This makes
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.send_part("#main")?;
/// client.part("#main")?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// equivalent to
///
/// ```no_run
/// # use circe::*
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_part(Command::PART("#main".to_string()))?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn send_part(&mut self, target: &str) -> Result<(), Error> {
pub fn part(&mut self, target: &str) -> Result<(), Error> {
self.write_command(Command::PART(target.to_string()))?;
Ok(())
}
/// Helper function for leaving the IRC server and shutting down the TCP stream afterwards.
/// This makes
/// Helper function for setting or getting the topic of a channel
/// ```no_run
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.send_quit(None)?;
/// client.topic("#main", Some("main channel"))?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// equivalent to
///
pub fn topic(&mut self, channel: &str, topic: Option<&str>) -> Result<(), Error> {
if let Some(topic) = topic {
self.write_command(Command::TOPIC(channel.to_string(), Some(topic.to_string())))?;
} else {
self.write_command(Command::TOPIC(channel.to_string(), None))?;
}
Ok(())
}
/// Helper function for leaving the IRC server and shutting down the TCP stream afterwards.
/// ```no_run
/// # use circe::*
/// # use circe::*;
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
/// client.write_part(Command::QUIT("Leaving...".to_string()))?;
/// client.stream.shutdown(std::net::Shutdown::Both)?;
/// client.quit(None)?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn send_quit(&mut self, message: Option<&str>) -> Result<(), Error> {
pub fn quit(&mut self, message: Option<&str>) -> Result<(), Error> {
if let Some(message) = message {
self.write_command(Command::QUIT(message.to_string()))?;
} else {
self.write_command(Command::QUIT(
format!("circe {} (https://crates.io/crates/circe)", env!("CARGO_PKG_VERSION")),
))?;
self.write_command(Command::QUIT(format!(
"circe {} (https://crates.io/crates/circe)",
env!("CARGO_PKG_VERSION")
)))?;
}
if self.config.ssl {
self.sslstream.as_ref().unwrap().shutdown().unwrap();
} else {
self.stream.unwrap().shutdown(std::net::Shutdown::Both)?;
}
self.stream.shutdown(std::net::Shutdown::Both)?;
Ok(())
}
@ -466,7 +693,7 @@ impl Config {
/// vec!["#main", "#circe"],
/// "192.168.178.100",
/// Some("+B"),
/// Some("IRSC"),
/// Some("circe"),
/// 6667,
/// "circe",
/// );
@ -477,21 +704,22 @@ impl Config {
mode: Option<&'static str>,
nickname: Option<&'static str>,
port: u16,
ssl: bool,
username: &str,
) -> Self {
// Conversion from &'static str to String
let channels_config = channels.iter().map(|channel| channel.to_string()).collect();
let mode_config: Option<String>;
if mode.is_some() {
mode_config = Some(mode.unwrap().to_string());
if let Some(mode) = mode {
mode_config = Some(mode.to_string());
} else {
mode_config = None;
}
let nickname_config: Option<String>;
if nickname.is_some() {
nickname_config = Some(nickname.unwrap().to_string());
if let Some(nickname) = nickname {
nickname_config = Some(nickname.to_string());
} else {
nickname_config = Some(username.to_string());
}
@ -502,6 +730,7 @@ impl Config {
mode: mode_config,
nickname: nickname_config,
port,
ssl,
username: username.into(),
}
}
@ -512,7 +741,6 @@ impl Config {
/// let config = Config::from_toml("config.toml")?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// ```toml
/// channels = ["#main", "#main2"]
/// host = "192.168.178.100"
@ -521,7 +749,6 @@ impl Config {
/// port = 6667
/// username = "circe"
/// ```
///
/// Returns an Error if the file cannot be opened or if the TOML is invalid
pub fn from_toml<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let mut file = File::open(&path)?;

View file

@ -29,12 +29,12 @@ fn main() -> Result<(), std::io::Error> {
}
if let Command::PRIVMSG(channel, message) = command {
if message.contains("!quit") {
client.send_quit(None)?;
client.quit(None)?;
break;
}
if message.contains("!close") {
client.send_part("#main")?;
client.part("#main")?;
}
println!("PRIVMSG received: {} {}", channel, message);
}