Beginning of rust implementation for tenquestionmarks.
This commit is contained in:
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name="tenquestionmarks"
|
||||
version="0.0.1"
|
||||
authors=["Adrian Malacoda <adrian.malacoda@monarch-pass.net>"]
|
||||
|
||||
[dependencies]
|
||||
hlua = "0.3"
|
||||
discord = "0.7.0"
|
||||
toml = "0.2.1"
|
@@ -0,0 +1,5 @@
|
||||
# tenquestionmarks chat bot
|
||||
tenquestionmarks is an extensible, scriptable chat bot. This iteration is written in rust.
|
||||
|
||||
## Architecture
|
||||
tenquestionmarks has two basic
|
||||
|
43
src/lib.rs
Normal file
43
src/lib.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
extern crate toml;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use toml::Table;
|
||||
|
||||
mod plugins;
|
||||
use plugins::Plugin;
|
||||
use plugins::loader::PluginLoader;
|
||||
use plugins::loader::PluginLoaderError;
|
||||
|
||||
pub struct Tenquestionmarks {
|
||||
plugins: BTreeMap<String, Box<Plugin>>
|
||||
}
|
||||
|
||||
impl Tenquestionmarks {
|
||||
pub fn with_plugins (plugins: BTreeMap<String, Box<Plugin>>) -> Tenquestionmarks {
|
||||
Tenquestionmarks {
|
||||
plugins: plugins
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, PluginLoaderError> {
|
||||
let loader = PluginLoader::new();
|
||||
let plugins = loader.load_from_configuration(configuration)?;
|
||||
Result::Ok(Tenquestionmarks::with_plugins(plugins))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Channel {
|
||||
name: String,
|
||||
description: String,
|
||||
topic: String
|
||||
}
|
||||
|
||||
pub struct User {
|
||||
name: String
|
||||
}
|
||||
|
||||
pub struct Message {
|
||||
sender: String,
|
||||
target: String,
|
||||
content: String
|
||||
}
|
41
src/main.rs
Normal file
41
src/main.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
extern crate tenquestionmarks;
|
||||
use tenquestionmarks::Tenquestionmarks;
|
||||
|
||||
extern crate toml;
|
||||
use toml::Parser;
|
||||
|
||||
fn main () {
|
||||
let configFileName = env::args().nth(1).unwrap_or("tenquestionmarks.toml".into());
|
||||
match File::open(&configFileName) {
|
||||
Ok(mut file) => {
|
||||
let mut contents = String::new();
|
||||
match file.read_to_string(&mut contents) {
|
||||
Ok(value) => {
|
||||
let mut parser = Parser::new(&contents);
|
||||
match parser.parse() {
|
||||
Some(configuration) => {
|
||||
println!("Loaded configuration from: {}", configFileName);
|
||||
match Tenquestionmarks::from_configuration(configuration) {
|
||||
Ok(tqm) => println!("loaded tenquestionmarks"),
|
||||
Err(e) => println!("Failed to initialize tenquestionmarks: {:?}", e)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
println!("Failed to parse config file {}: {:?}. Config file must be a valid TOML file.", configFileName, parser.errors);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Failed to open config file {}: {:?}", configFileName, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Failed to open config file! Please specify path to a config file.");
|
||||
}
|
||||
}
|
||||
}
|
21
src/plugins/discord.rs
Normal file
21
src/plugins/discord.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use plugins::Plugin;
|
||||
use toml::Table;
|
||||
|
||||
pub struct DiscordPlugin {
|
||||
|
||||
}
|
||||
|
||||
impl DiscordPlugin {
|
||||
pub fn new (configuration: &Table) -> Box<Plugin> {
|
||||
match configuration.get("channel") {
|
||||
Some(channel) => println!("Using channel: {}", channel),
|
||||
None => println!("No channel specified")
|
||||
}
|
||||
|
||||
Box::new(DiscordPlugin {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for DiscordPlugin {
|
||||
|
||||
}
|
23
src/plugins/hello.rs
Normal file
23
src/plugins/hello.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use plugins::Plugin;
|
||||
use toml::Table;
|
||||
|
||||
pub struct Hello {
|
||||
name: String
|
||||
}
|
||||
|
||||
impl Hello {
|
||||
pub fn new (configuration: &Table) -> Box<Plugin> {
|
||||
let name = configuration.get("name")
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or("world");
|
||||
|
||||
println!("Hello, {}!", name);
|
||||
Box::new(Hello {
|
||||
name: String::from(name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for Hello {
|
||||
|
||||
}
|
70
src/plugins/loader.rs
Normal file
70
src/plugins/loader.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use toml::Table;
|
||||
|
||||
use plugins::Plugin;
|
||||
use plugins::hello::Hello;
|
||||
use plugins::discord::DiscordPlugin;
|
||||
use plugins::lua::LuaPlugin;
|
||||
|
||||
pub struct PluginLoader {
|
||||
types: BTreeMap<&'static str, fn(&Table) -> Box<Plugin>>
|
||||
}
|
||||
|
||||
impl PluginLoader {
|
||||
pub fn new () -> PluginLoader {
|
||||
let mut types = BTreeMap::new();
|
||||
types.insert("hello", Hello::new as fn(&Table) -> Box<Plugin>);
|
||||
types.insert("discord", DiscordPlugin::new as fn(&Table) -> Box<Plugin>);
|
||||
types.insert("lua", LuaPlugin::new as fn(&Table) -> Box<Plugin>);
|
||||
PluginLoader {
|
||||
types: types
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_configuration (&self, configuration: Table) -> Result<BTreeMap<String, Box<Plugin>>, PluginLoaderError> {
|
||||
configuration.into_iter().map(|(key, value)| {
|
||||
match value.as_table() {
|
||||
Some(table) => {
|
||||
let plugin = self.load_single_plugin(&key, table)?;
|
||||
Result::Ok((key, plugin))
|
||||
},
|
||||
None => Result::Err(PluginLoaderError { message: format!("Bad configuration parameters for plugin instance: {}. Configuration for a plugin must be a table.", key) })
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn load_single_plugin (&self, name: &str, configuration: &Table) -> Result<Box<Plugin>, PluginLoaderError> {
|
||||
/*
|
||||
* The plugin type defaults to the instance name (in the tenquestionmarks configuration)
|
||||
* but can explicitly be set by using the special "type" parameter.
|
||||
*/
|
||||
let plugin_type: &str = configuration.get("type")
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or(name);
|
||||
|
||||
match self.types.get(plugin_type) {
|
||||
Some(constructor) => Result::Ok(constructor(configuration)),
|
||||
None => Result::Err(PluginLoaderError { message: format!("No such plugin type: {}", plugin_type) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginLoaderError {
|
||||
message: String
|
||||
}
|
||||
|
||||
impl Error for PluginLoaderError {
|
||||
fn description(&self) -> &str {
|
||||
&self.message[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PluginLoaderError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PluginLoaderError: {}", self.message)
|
||||
}
|
||||
}
|
16
src/plugins/lua.rs
Normal file
16
src/plugins/lua.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use plugins::Plugin;
|
||||
use toml::Table;
|
||||
|
||||
pub struct LuaPlugin {
|
||||
|
||||
}
|
||||
|
||||
impl LuaPlugin {
|
||||
pub fn new (configuration: &Table) -> Box<Plugin> {
|
||||
Box::new(LuaPlugin {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for LuaPlugin {
|
||||
|
||||
}
|
14
src/plugins/mod.rs
Normal file
14
src/plugins/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use toml::Value;
|
||||
use toml::Table;
|
||||
|
||||
pub mod hello;
|
||||
pub mod lua;
|
||||
pub mod discord;
|
||||
|
||||
pub mod loader;
|
||||
|
||||
pub trait Plugin {
|
||||
|
||||
}
|
12
tenquestionmarks.toml
Normal file
12
tenquestionmarks.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[hello]
|
||||
|
||||
[hello-dave]
|
||||
type = "hello"
|
||||
name = "Dave"
|
||||
|
||||
[hello-fred]
|
||||
type = "hello"
|
||||
name = "Fred"
|
||||
|
||||
[discord]
|
||||
channel = "#testchannelpleaseignore"
|
Reference in New Issue
Block a user