mirror of
https://github.com/Bayselonarrend/OpenIntegrations.git
synced 2024-12-29 02:57:35 +02:00
Удален tcpc, доработана вк Mongo
This commit is contained in:
parent
c88357604f
commit
c39aef4ecf
3
src/addins/mongo/.cargo/config.toml
Normal file
3
src/addins/mongo/.cargo/config.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "C:/msys64/mingw64/bin/x86_64-w64-mingw32-gcc.exe"
|
||||
rustc-linker = "C:/msys64/mingw64/bin/x86_64-w64-mingw32-gcc.exe"
|
@ -3,6 +3,8 @@ name = "opi_mongodb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
@ -11,6 +13,7 @@ lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = "abort" # Abort on panic
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
opt-level = "z"
|
||||
|
||||
[dependencies]
|
||||
addin1c = "0.5.0"
|
||||
@ -20,6 +23,7 @@ serde_json = "1.0.133"
|
||||
|
||||
|
||||
[dependencies.mongodb]
|
||||
version = "2.0.0"
|
||||
version = "=2.8.2"
|
||||
default-features = false
|
||||
features = ["sync"]
|
||||
|
||||
|
2
src/addins/mongo/release.bat
Normal file
2
src/addins/mongo/release.bat
Normal file
@ -0,0 +1,2 @@
|
||||
cargo build --release --target x86_64-pc-windows-msvc
|
||||
cargo build --release --target x86_64-unknown-linux-gnu
|
@ -1,84 +1,557 @@
|
||||
use mongodb::{options::ClientOptions, bson::{doc, Document}};
|
||||
use mongodb::sync::{Client, Collection};
|
||||
use serde_json::Value;
|
||||
use mongodb::{options::{ClientOptions, FindOptions}, bson::{doc, Document}};
|
||||
use mongodb::sync::{Client, Collection, Database};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
pub struct MongoClient {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl MongoClient {
|
||||
|
||||
// СЛУЖЕБНЫЕ МЕТОДЫ ----------------------------------------------------------------------------
|
||||
|
||||
// Конструктор для создания клиента
|
||||
pub fn new(uri: &str) -> MongoClient {
|
||||
|
||||
match ClientOptions::parse(uri) {
|
||||
Ok(client_options) => match Client::with_options(client_options) {
|
||||
Ok(client) => MongoClient { client },
|
||||
Err(_) => MongoClient { client: Client::with_options(ClientOptions::default()).unwrap() }, // Возвращаем дефолтный клиент в случае ошибки
|
||||
Err(_) => MongoClient { client: Client::with_options(ClientOptions::default()).unwrap() },
|
||||
},
|
||||
Err(_) => MongoClient { client: Client::with_options(ClientOptions::default()).unwrap() }, // Возвращаем дефолтный клиент в случае ошибки
|
||||
Err(_) => MongoClient { client: Client::with_options(ClientOptions::default()).unwrap() },
|
||||
}
|
||||
}
|
||||
|
||||
// Синхронный метод для вставки данных, возвращающий строку или ошибку
|
||||
// Вспомогательная функция для формирования JSON ответа
|
||||
fn make_response(ok: bool, data: &str) -> String {
|
||||
json!({
|
||||
"ok": ok,
|
||||
"data": data
|
||||
})
|
||||
.to_string()
|
||||
}
|
||||
|
||||
// ОСНОВНЫЕ МЕТОДЫ -----------------------------------------------------------------------------
|
||||
|
||||
// РАБОТА С БАЗАМИ ДАННЫХ ----------------------------------------------------------------------
|
||||
|
||||
// Получение списка баз
|
||||
pub fn list_databases(&self) -> String {
|
||||
match self.client.list_database_names(None, None) {
|
||||
Ok(databases) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("{:?}", databases)),
|
||||
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to list databases: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Получение статистики базы данных
|
||||
pub fn database_stats(&self, db_name: &str) -> String {
|
||||
let db = self.client.database(db_name);
|
||||
match db.run_command(doc! { "dbStats": 1 }, None) {
|
||||
Ok(stats) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("{:?}", stats)),
|
||||
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to get database stats for '{}': {}", db_name, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Удаление базы
|
||||
pub fn drop_database(&self, db_name: &str) -> String {
|
||||
match self.client.database(db_name).drop(None) {
|
||||
Ok(_) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("Database '{}' dropped successfully", db_name)),
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to drop database '{}': {}", db_name, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка существования базы
|
||||
pub fn database_exists(&self, db_name: &str) -> bool {
|
||||
// Получаем список всех баз данных
|
||||
let db_list = match self.client.list_databases(None, None) {
|
||||
Ok(databases) => databases,
|
||||
Err(_) => return false, // Если ошибка при получении списка баз, возвращаем false
|
||||
};
|
||||
|
||||
// Проверяем, есть ли указанная база данных в списке
|
||||
db_list.iter().any(|db| db.name == db_name)
|
||||
}
|
||||
|
||||
// РАБОТА С КОЛЛЕКЦИЯМИ ------------------------------------------------------------------------
|
||||
|
||||
// Получение списка коллекций
|
||||
pub fn list_collections(&self, db_name: &str) -> String {
|
||||
let db: Database = self.client.database(db_name);
|
||||
|
||||
match db.list_collection_names(None) {
|
||||
Ok(collection_names) => {
|
||||
// Преобразуем список коллекций в JSON
|
||||
let collections_json = serde_json::to_string(&collection_names).unwrap();
|
||||
Self::make_response(true, &collections_json)
|
||||
}
|
||||
Err(err) => Self::make_response(false, &format!("Failed to list collections: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Создание коллекции в базе данных
|
||||
pub fn create_collection(&self, db_name: &str, collection_name: &str) -> String {
|
||||
let db = self.client.database(db_name);
|
||||
match db.create_collection(collection_name, None) {
|
||||
Ok(_) => Self::make_response(
|
||||
true,
|
||||
&format!("Collection '{}' created successfully in database '{}'", collection_name, db_name)),
|
||||
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to create collection '{}': {}", collection_name, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Удаление коллекции из базы данных
|
||||
pub fn drop_collection(&self, db_name: &str, collection_name: &str) -> String {
|
||||
let collection = self.client.database(db_name).collection::<Document>(collection_name);
|
||||
match collection.drop(None) {
|
||||
Ok(_) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("Collection '{}' dropped successfully from database '{}'", collection_name, db_name)),
|
||||
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to drop collection '{}': {}", collection_name, err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка, существует ли коллекция
|
||||
pub fn collection_exists(&self, db_name: &str, collection_name: &str) -> String {
|
||||
match self.client.database(db_name).list_collection_names(None) {
|
||||
Ok(collections) => {
|
||||
if collections.contains(&collection_name.to_string()) {
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("Collection '{}' exists in database '{}'", collection_name, db_name))
|
||||
} else {
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Collection '{}' does not exist in database '{}'", collection_name, db_name))
|
||||
}
|
||||
}
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Failed to list collections: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// РАБОТА С ДОКУМЕНТАМИ ------------------------------------------------------------------------
|
||||
|
||||
// ДОБАВЛЕНИЕ ДОКУМЕНТОВ -----------------------------------------------------------------------
|
||||
|
||||
// Метод для вставки одного документа
|
||||
pub fn insert_data(&self, db_name: &str, collection_name: &str, data: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Преобразуем строку JSON в объект
|
||||
let json_value: Value = match serde_json::from_str(data) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return format!("Failed to parse JSON: {}", err),
|
||||
Err(err) =>
|
||||
return Self::make_response(
|
||||
false,
|
||||
&format!("Failed to parse JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем объект JSON в BSON
|
||||
let bson_document = match bson::to_bson(&json_value) {
|
||||
Ok(bson) => bson,
|
||||
Err(err) => return format!("Failed to convert to BSON: {}", err),
|
||||
Err(err) =>
|
||||
return Self::make_response(
|
||||
false,
|
||||
&format!("Failed to convert to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Вставляем BSON документ в коллекцию
|
||||
match collection.insert_one(bson_document.as_document().unwrap(), None) {
|
||||
Ok(_) => "Insert successful".to_string(),
|
||||
Err(err) => format!("Insert failed: {}", err),
|
||||
Ok(_) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
"Insert successful"),
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Insert failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Синхронный метод для поиска данных, возвращающий строку или ошибку
|
||||
// Метод для вставки нескольких документов
|
||||
pub fn insert_many(&self, db_name: &str, collection_name: &str, data: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
let json_values: Vec<Value> = match serde_json::from_str(data) {
|
||||
Ok(values) => values,
|
||||
Err(err) =>
|
||||
return Self::make_response(
|
||||
false,
|
||||
&format!("Failed to parse JSON array: {}", err)),
|
||||
};
|
||||
|
||||
let bson_documents: Vec<Document> = match json_values
|
||||
.into_iter()
|
||||
.map(|value| bson::to_bson(&value))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
{
|
||||
Ok(bson_array) => bson_array
|
||||
.into_iter()
|
||||
.filter_map(|bson| bson.as_document().cloned())
|
||||
.collect(),
|
||||
Err(err) =>
|
||||
return Self::make_response(
|
||||
false,
|
||||
&format!("Failed to convert JSON to BSON: {}", err)),
|
||||
};
|
||||
|
||||
match collection.insert_many(bson_documents, None) {
|
||||
Ok(result) =>
|
||||
Self::make_response(
|
||||
true,
|
||||
&format!("Insert successful: {} documents inserted", result.inserted_ids.len())),
|
||||
Err(err) =>
|
||||
Self::make_response(
|
||||
false,
|
||||
&format!("Insert failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// ОБНОВЛЕНИЕ ДОКУМЕНТОВ -----------------------------------------------------------------------
|
||||
|
||||
// Обновление одного документа в коллекции
|
||||
pub fn update_data(&self, db_name: &str, collection_name: &str, query: &str, update: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Парсим JSON в структуру Value
|
||||
let bson_query: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
let bson_update: Value = match serde_json::from_str(update) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse update JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем в BSON документы
|
||||
let bson_query_doc = bson::to_document(&bson_query).unwrap();
|
||||
let bson_update_doc = bson::to_document(&bson_update).unwrap();
|
||||
|
||||
// Преобразуем в UpdateModifications
|
||||
let update_modifications = doc! {
|
||||
"$set": bson_update_doc
|
||||
};
|
||||
|
||||
// Обновляем один документ
|
||||
match collection.update_one(bson_query_doc, update_modifications, None) {
|
||||
Ok(_) => Self::make_response(true, "Update successful"),
|
||||
Err(err) => Self::make_response(false, &format!("Update failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление нескольких документов
|
||||
pub fn update_many(&self, db_name: &str, collection_name: &str, query: &str, update: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Парсим JSON в структуру Value
|
||||
let bson_query: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
let bson_update: Value = match serde_json::from_str(update) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse update JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем в BSON документы
|
||||
let bson_query_doc = bson::to_document(&bson_query).unwrap();
|
||||
let bson_update_doc = bson::to_document(&bson_update).unwrap();
|
||||
|
||||
// Преобразуем в UpdateModifications
|
||||
let update_modifications = doc! {"$set": bson_update_doc};
|
||||
|
||||
// Обновляем все документы, которые соответствуют запросу
|
||||
match collection.update_many(bson_query_doc, update_modifications, None) {
|
||||
Ok(result) => Self::make_response(
|
||||
true,
|
||||
&format!("Update successful: {} documents updated", result.modified_count)
|
||||
),
|
||||
Err(err) => Self::make_response(false, &format!("Update failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// УДАЛЕНИЕ ДОКУМЕНТОВ -------------------------------------------------------------------------
|
||||
|
||||
// Удаление одного документа
|
||||
pub fn delete_data(&self, db_name: &str, collection_name: &str, query: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Преобразуем строку JSON в объект
|
||||
let query_value: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем объект JSON в BSON
|
||||
let bson_query = match bson::to_bson(&query_value) {
|
||||
Ok(bson) => bson,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert query to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Проверяем, что BSON является документом
|
||||
match bson_query.as_document() {
|
||||
Some(bson_query_doc) => {
|
||||
// Удаляем один документ
|
||||
match collection.delete_one(bson_query_doc.clone(), None) {
|
||||
Ok(result) => {
|
||||
if result.deleted_count > 0 {
|
||||
Self::make_response(true, "Delete successful")
|
||||
} else {
|
||||
Self::make_response(false, "No documents matched the query")
|
||||
}
|
||||
}
|
||||
Err(err) => Self::make_response(false, &format!("Delete failed: {}", err)),
|
||||
}
|
||||
}
|
||||
None => Self::make_response(false, "Query is not a valid BSON document"),
|
||||
}
|
||||
}
|
||||
|
||||
// Удаление нескольких документов
|
||||
pub fn delete_many(&self, db_name: &str, collection_name: &str, query: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Преобразуем строку JSON в объект
|
||||
let query_value: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем объект JSON в BSON
|
||||
let bson_query = match bson::to_bson(&query_value) {
|
||||
Ok(bson) => bson,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert query to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Проверяем, что BSON является документом
|
||||
match bson_query.as_document() {
|
||||
Some(bson_query_doc) => {
|
||||
// Удаляем несколько документов
|
||||
match collection.delete_many(bson_query_doc.clone(), None) {
|
||||
Ok(result) => Self::make_response(
|
||||
true,
|
||||
&format!("Delete successful: {} documents deleted", result.deleted_count),
|
||||
),
|
||||
Err(err) => Self::make_response(false, &format!("Delete failed: {}", err)),
|
||||
}
|
||||
}
|
||||
None => Self::make_response(false, "Query is not a valid BSON document"),
|
||||
}
|
||||
}
|
||||
|
||||
// ПОИСК ДОКУМЕНТОВ ----------------------------------------------------------------------------
|
||||
|
||||
// Поиск одного документа
|
||||
pub fn find_data(&self, db_name: &str, collection_name: &str, query: &str) -> String {
|
||||
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
let filter = doc! { "name": query };
|
||||
|
||||
match collection.find_one(filter, None) {
|
||||
Ok(Some(doc)) => match doc.get_str("name") {
|
||||
Ok(name) => name.to_string(),
|
||||
Err(_) => "Error reading name from document".to_string(),
|
||||
},
|
||||
Ok(None) => "Document not found".to_string(),
|
||||
Err(err) => format!("Find failed: {}", err),
|
||||
Ok(Some(doc)) => Self::make_response(true, &format!("{:?}", doc)),
|
||||
Ok(None) => Self::make_response(false, "Document not found"),
|
||||
Err(err) => Self::make_response(false, &format!("Find failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Синхронный метод для подсчета документов, возвращающий количество или ошибку
|
||||
pub fn count_documents(&self, db_name: &str, collection_name: &str) -> i32 {
|
||||
// Поиск нескольких документов с пагинацией
|
||||
pub fn find_many(&self, db_name: &str, collection_name: &str, query: &str, page: i32, page_size: i32) -> String {
|
||||
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
match collection.count_documents(None, None) {
|
||||
Ok(count) => count as i32,
|
||||
Err(_) => -1, // Возвращаем -1 в случае ошибки
|
||||
// Парсим JSON в структуру Value
|
||||
let bson_query: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем JSON в BSON документ
|
||||
let bson_query_doc = match bson::to_document(&bson_query) {
|
||||
Ok(doc) => doc,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert query to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Создаем параметры запроса с пагинацией
|
||||
let skip = page * page_size;
|
||||
let find_options = FindOptions::builder()
|
||||
.skip(skip as u64) // Пропустить количество документов
|
||||
.limit(page_size as i64) // Ограничить количество документов
|
||||
.build();
|
||||
|
||||
// Выполняем поиск
|
||||
let cursor = match collection.find(bson_query_doc, find_options) {
|
||||
Ok(cursor) => cursor,
|
||||
Err(err) => return Self::make_response(false, &format!("Find failed: {}", err)),
|
||||
};
|
||||
|
||||
// Собираем все документы из курсора в вектор
|
||||
let mut results = Vec::new();
|
||||
for result in cursor {
|
||||
match result {
|
||||
Ok(doc) => results.push(doc),
|
||||
Err(err) => return Self::make_response(false, &format!("Error iterating documents: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразуем документы в JSON строку
|
||||
let json_results = match serde_json::to_string(&results) {
|
||||
Ok(json) => json,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert documents to JSON: {}", err)),
|
||||
};
|
||||
|
||||
Self::make_response(true, &json_results)
|
||||
}
|
||||
|
||||
// Синхронный метод для проверки наличия документа, возвращающий булево результат
|
||||
pub fn document_exists(&self, db_name: &str, collection_name: &str, query: &str) -> bool {
|
||||
// Поиск всех документов с пагинацией
|
||||
pub fn find_all(&self, db_name: &str, collection_name: &str, query: &str, page: i32, page_size: i32) -> String {
|
||||
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Парсим JSON в структуру Value
|
||||
let bson_query: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Преобразуем JSON в BSON документ
|
||||
let bson_query_doc = match bson::to_document(&bson_query) {
|
||||
Ok(doc) => doc,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert query to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Создаем параметры запроса с пагинацией
|
||||
let skip = page * page_size;
|
||||
let find_options = FindOptions::builder()
|
||||
.skip(skip as u64) // Пропустить количество документов
|
||||
.limit(page_size as i64) // Ограничить количество документов
|
||||
.build();
|
||||
|
||||
// Выполняем поиск
|
||||
let cursor = match collection.find(bson_query_doc, find_options) {
|
||||
Ok(cursor) => cursor,
|
||||
Err(err) => return Self::make_response(false, &format!("Find failed: {}", err)),
|
||||
};
|
||||
|
||||
// Собираем все документы из курсора в вектор
|
||||
let mut results = Vec::new();
|
||||
for result in cursor {
|
||||
match result {
|
||||
Ok(doc) => results.push(doc),
|
||||
Err(err) => return Self::make_response(false, &format!("Error iterating documents: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразуем документы в JSON строку
|
||||
let json_results = match serde_json::to_string(&results) {
|
||||
Ok(json) => json,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert documents to JSON: {}", err)),
|
||||
};
|
||||
|
||||
Self::make_response(true, &json_results)
|
||||
}
|
||||
|
||||
// Проверка существования документа
|
||||
pub fn document_exists(&self, db_name: &str, collection_name: &str, query: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
let filter = doc! { "name": query };
|
||||
|
||||
match collection.find_one(filter, None) {
|
||||
Ok(Some(_)) => true,
|
||||
Ok(None) => false,
|
||||
Err(_) => false, // Возвращаем false в случае ошибки
|
||||
Ok(Some(_)) => Self::make_response(true, "true"),
|
||||
Ok(None) => Self::make_response(false, "false"),
|
||||
Err(err) => Self::make_response(false, &format!("Check failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Количество документов в коллекции
|
||||
pub fn count_documents(&self, db_name: &str, collection_name: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
match collection.count_documents(None, None) {
|
||||
Ok(count) => Self::make_response(true, &count.to_string()),
|
||||
Err(err) => Self::make_response(false, &format!("Count failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// ДРУГОЕ --------------------------------------------------------------------------------------
|
||||
|
||||
// Произвольный запрос
|
||||
pub fn run_custom_query(&self, db_name: &str, query: &str) -> String {
|
||||
let db = self.client.database(db_name);
|
||||
|
||||
// Парсим строку запроса в BSON
|
||||
let bson_query: Value = match serde_json::from_str(query) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse query JSON: {}", err)),
|
||||
};
|
||||
|
||||
let bson_query_doc = match bson::to_document(&bson_query) {
|
||||
Ok(doc) => doc,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to convert query to BSON: {}", err)),
|
||||
};
|
||||
|
||||
// Выполнение произвольного запроса
|
||||
match db.run_command(bson_query_doc, None) {
|
||||
Ok(result) => Self::make_response(true, &format!("{:?}", result)),
|
||||
Err(err) => Self::make_response(false, &format!("Query execution failed: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Агрегация
|
||||
pub fn aggregate(&self, db_name: &str, collection_name: &str, pipeline: &str) -> String {
|
||||
let collection: Collection<Document> = self.client.database(db_name).collection(collection_name);
|
||||
|
||||
// Парсим строку JSON в массив стадий агрегации
|
||||
let bson_pipeline: Vec<Document> = match serde_json::from_str(pipeline) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to parse pipeline JSON: {}", err)),
|
||||
};
|
||||
|
||||
// Выполняем агрегацию
|
||||
let cursor = match collection.aggregate(bson_pipeline, None) {
|
||||
Ok(cursor) => cursor,
|
||||
Err(err) => return Self::make_response(false, &format!("Aggregation failed: {}", err)),
|
||||
};
|
||||
|
||||
// Собираем результат в вектор
|
||||
let mut result: Vec<Value> = Vec::new();
|
||||
for document in cursor {
|
||||
match document {
|
||||
Ok(doc) => result.push(serde_json::to_value(doc).unwrap()),
|
||||
Err(err) => return Self::make_response(false, &format!("Failed to read document: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// Возвращаем результат как строку
|
||||
Self::make_response(true, &serde_json::to_string(&result).unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -7,20 +7,54 @@ use crate::core::getset;
|
||||
|
||||
// Синонимы
|
||||
pub const METHODS: &[&[u16]] = &[
|
||||
name!("InsertData"), // 2
|
||||
name!("FindData"), // 3
|
||||
name!("CountDocuments"), // 4
|
||||
name!("DocumentExists"), // 5
|
||||
name!("ListDatabases"), // 0
|
||||
name!("DatabaseStats"), // 1
|
||||
name!("DropDatabase"), // 2
|
||||
name!("DatabaseExists"), // 3
|
||||
name!("ListCollections"), // 4
|
||||
name!("CreateCollection"), // 5
|
||||
name!("DropCollection"), // 6
|
||||
name!("CollectionExists"), // 7
|
||||
name!("InsertData"), // 8
|
||||
name!("InsertMany"), // 9
|
||||
name!("UpdateData"), // 10
|
||||
name!("UpdateMany"), // 11
|
||||
name!("DeleteData"), // 12
|
||||
name!("DeleteMany"), // 13
|
||||
name!("FindData"), // 14
|
||||
name!("FindMany"), // 15
|
||||
name!("FindAll"), // 16
|
||||
name!("DocumentExists"), // 17
|
||||
name!("CountDocuments"), // 18
|
||||
name!("RunCustomQuery"), // 19
|
||||
name!("Aggregate"), // 20
|
||||
];
|
||||
|
||||
// Число параметров функций компоненты
|
||||
pub fn get_params_amount(num: usize) -> usize {
|
||||
match num {
|
||||
0 => 3,
|
||||
1 => 3,
|
||||
2 => 2,
|
||||
3 => 2,
|
||||
_ => 0,
|
||||
0 => 0, // list_databases (нет параметров)
|
||||
1 => 1, // database_stats
|
||||
2 => 1, // drop_database
|
||||
3 => 1, // database_exists
|
||||
4 => 1, // list_collections
|
||||
5 => 2, // create_collection
|
||||
6 => 2, // drop_collection
|
||||
7 => 2, // collection_exists
|
||||
8 => 3, // insert_data
|
||||
9 => 3, // insert_many
|
||||
10 => 4, // update_data
|
||||
11 => 4, // update_many
|
||||
12 => 3, // delete_data
|
||||
13 => 3, // delete_many
|
||||
14 => 3, // find_data
|
||||
15 => 5, // find_many
|
||||
16 => 5, // find_all
|
||||
17 => 3, // document_exists
|
||||
18 => 2, // count_documents
|
||||
19 => 2, // run_custom_query
|
||||
20 => 3, // aggregate
|
||||
_ => 0, // по умолчанию 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,44 +66,125 @@ pub fn cal_func(obj: &AddIn, num: usize, params: &mut [Variant]) -> Box<dyn crat
|
||||
let client = methods::MongoClient::new(address);
|
||||
|
||||
match num {
|
||||
0 => {
|
||||
|
||||
0 => { // list_databases
|
||||
Box::new(client.list_databases())
|
||||
},
|
||||
1 => { // database_stats
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name= params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.database_stats(&db_name))
|
||||
},
|
||||
2 => { // drop_database
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
Box::new(client.drop_database(&db_name))
|
||||
},
|
||||
3 => { // database_exists
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
Box::new(client.database_exists(&db_name))
|
||||
},
|
||||
4 => { // list_collections
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
Box::new(client.list_collections(&db_name))
|
||||
},
|
||||
5 => { // create_collection
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.create_collection(&db_name, &collection_name))
|
||||
},
|
||||
6 => { // drop_collection
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.drop_collection(&db_name, &collection_name))
|
||||
},
|
||||
7 => { // collection_exists
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.collection_exists(&db_name, &collection_name))
|
||||
},
|
||||
8 => { // insert_data
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let data = params[2].get_string().unwrap_or(String::new());
|
||||
|
||||
Box::new(client.insert_data(&db_name, &collection_name, &data))
|
||||
},
|
||||
|
||||
1 => {
|
||||
|
||||
9 => { // insert_many
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name= params[1].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let data = params[2].get_string().unwrap_or(String::new());
|
||||
Box::new(client.insert_many(&db_name, &collection_name, &data))
|
||||
},
|
||||
10 => { // update_data
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
let update = params[3].get_string().unwrap_or(String::new());
|
||||
Box::new(client.update_data(&db_name, &collection_name, &query, &update))
|
||||
},
|
||||
11 => { // update_many
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
let update = params[3].get_string().unwrap_or(String::new());
|
||||
Box::new(client.update_many(&db_name, &collection_name, &query, &update))
|
||||
},
|
||||
12 => { // delete_data
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
Box::new(client.delete_data(&db_name, &collection_name, &query))
|
||||
},
|
||||
13 => { // delete_many
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
Box::new(client.delete_many(&db_name, &collection_name, &query))
|
||||
},
|
||||
14 => { // find_data
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
|
||||
Box::new(client.find_data(&db_name, &collection_name, &query))
|
||||
|
||||
},
|
||||
|
||||
2 => {
|
||||
|
||||
15 => { // find_many
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name= params[1].get_string().unwrap_or(String::new());
|
||||
|
||||
Box::new(client.count_documents(&db_name, &collection_name))
|
||||
|
||||
},
|
||||
|
||||
3 => {
|
||||
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name= params[1].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
let page = params[3].get_i32().unwrap_or(0);
|
||||
let page_size = params[4].get_i32().unwrap_or(0) as i32;
|
||||
Box::new(client.find_many(&db_name, &collection_name, &query, page, page_size))
|
||||
},
|
||||
16 => { // find_all
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
let page = params[3].get_i32().unwrap_or(0);
|
||||
let page_size = params[4].get_i32().unwrap_or(0);
|
||||
Box::new(client.find_all(&db_name, &collection_name, &query, page, page_size))
|
||||
},
|
||||
17 => { // document_exists
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let query = params[2].get_string().unwrap_or(String::new());
|
||||
|
||||
Box::new(client.document_exists(&db_name, &collection_name, &query))
|
||||
|
||||
},
|
||||
18 => { // count_documents
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.count_documents(&db_name, &collection_name))
|
||||
},
|
||||
19 => { // run_custom_query
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let query = params[1].get_string().unwrap_or(String::new());
|
||||
Box::new(client.run_custom_query(&db_name, &query))
|
||||
},
|
||||
20 => { // aggregate
|
||||
let db_name = params[0].get_string().unwrap_or(String::new());
|
||||
let collection_name = params[1].get_string().unwrap_or(String::new());
|
||||
let pipeline = params[2].get_string().unwrap_or(String::new());
|
||||
Box::new(client.aggregate(&db_name, &collection_name, &pipeline))
|
||||
},
|
||||
_ => {
|
||||
Box::new(false)
|
||||
}
|
||||
_ => Box::new(false),
|
||||
}
|
||||
|
||||
}
|
||||
|
379
src/addins/tcp_client/Cargo.lock
generated
379
src/addins/tcp_client/Cargo.lock
generated
@ -1,379 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addin1c"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce4a2faf46b8c80fc9f6a9a280bef80b7968d6b5d4324b563e6b8b4717031c3"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"utf16_lit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[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.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opi_tcp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"addin1c",
|
||||
"native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_lit"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "opi_tcp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = "abort" # Abort on panic
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
|
||||
[dependencies]
|
||||
addin1c = "0.1"
|
||||
native-tls = "0.2.12"
|
@ -1,46 +0,0 @@
|
||||
mod opi_tcp;
|
||||
|
||||
use std::{
|
||||
ffi::{c_int, c_long, c_void},
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use opi_tcp::OpiTcp;
|
||||
use addin1c::{create_component, destroy_component, name, AttachType};
|
||||
|
||||
pub static mut PLATFORM_CAPABILITIES: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn GetClassObject(name: *const u16, component: *mut *mut c_void) -> c_long {
|
||||
|
||||
let addin = OpiTcp::new();
|
||||
create_component(component, addin)
|
||||
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn DestroyObject(component: *mut *mut c_void) -> c_long {
|
||||
destroy_component(component)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn GetClassNames() -> *const u16 {
|
||||
// small strings for performance
|
||||
name!("Client").as_ptr()
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SetPlatformCapabilities(capabilities: c_int) -> c_int {
|
||||
PLATFORM_CAPABILITIES.store(capabilities, Ordering::Relaxed);
|
||||
3
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn GetAttachType() -> AttachType {
|
||||
AttachType::Any
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
use addin1c::{name, ParamValue, RawAddin, Tm, Variant};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::{TcpStream, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
use native_tls::{TlsConnector, TlsStream};
|
||||
|
||||
// Свойства объекта
|
||||
const PROPS: &[&[u16]] = &[
|
||||
name!("Адрес"),
|
||||
name!("Порт"),
|
||||
name!("SSL")
|
||||
|
||||
];
|
||||
|
||||
// Объявление методов
|
||||
const METHODS: &[&[u16]] = &[
|
||||
name!("ОтправитьСообщение")
|
||||
];
|
||||
|
||||
// Определение типов свойств
|
||||
pub struct OpiTcp {
|
||||
address: String,
|
||||
port: i32,
|
||||
ssl: bool
|
||||
}
|
||||
|
||||
// Конструктор
|
||||
impl OpiTcp {
|
||||
pub fn new() -> OpiTcp {
|
||||
OpiTcp {
|
||||
address: String::from(""),
|
||||
port: 0,
|
||||
ssl: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка удаления объекта
|
||||
impl Drop for OpiTcp {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
// Определение класса
|
||||
impl RawAddin for OpiTcp {
|
||||
fn register_extension_as(&mut self) -> &'static [u16] {
|
||||
name!("Client")
|
||||
}
|
||||
|
||||
fn get_n_props(&mut self) -> usize {
|
||||
PROPS.len()
|
||||
}
|
||||
|
||||
fn find_prop(&mut self, name: &[u16]) -> Option<usize> {
|
||||
PROPS.iter().position(|&x| x == name)
|
||||
}
|
||||
|
||||
fn get_prop_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> {
|
||||
PROPS.get(num).copied()
|
||||
}
|
||||
|
||||
// Геттеры
|
||||
fn get_prop_val(&mut self, num: usize, val: &mut Variant) -> bool {
|
||||
match num {
|
||||
0 => {
|
||||
let s: Vec<u16> = self.address.encode_utf16().collect();
|
||||
return val.set_str(s.as_slice());
|
||||
}
|
||||
1 => val.set_i32(self.port),
|
||||
2 => val.set_bool(self.ssl),
|
||||
_ => return false,
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
// Сеттеры
|
||||
fn set_prop_val(&mut self, num: usize, val: &ParamValue) -> bool {
|
||||
match num {
|
||||
0 => match val {
|
||||
ParamValue::Str(x) => {
|
||||
self.address = String::from_utf16(x).unwrap();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
1 => match val {
|
||||
ParamValue::I32(x) => {
|
||||
self.port = *x;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
2 => match val {
|
||||
ParamValue::Bool(x) =>{
|
||||
self.ssl = *x;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_prop_readable(&mut self, _num: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_prop_writable(&mut self, num: usize) -> bool {
|
||||
match num {
|
||||
0 => true,
|
||||
1 => true,
|
||||
2 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_n_methods(&mut self) -> usize {
|
||||
METHODS.len()
|
||||
}
|
||||
|
||||
fn find_method(&mut self, name: &[u16]) -> Option<usize> {
|
||||
METHODS.iter().position(|&x| x == name)
|
||||
}
|
||||
|
||||
fn get_method_name(&mut self, num: usize, _alias: usize) -> Option<&'static [u16]> {
|
||||
METHODS.get(num).copied()
|
||||
}
|
||||
|
||||
fn get_n_params(&mut self, num: usize) -> usize {
|
||||
match num {
|
||||
0 => 2,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_param_def_value(
|
||||
&mut self,
|
||||
_method_num: usize,
|
||||
_param_num: usize,
|
||||
_value: Variant,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn has_ret_val(&mut self, num: usize) -> bool {
|
||||
match num {
|
||||
0 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn call_as_proc(&mut self, _num: usize, _params: &mut [Variant]) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn call_as_func(
|
||||
&mut self,
|
||||
num: usize,
|
||||
params: &mut [Variant],
|
||||
ret_value: &mut Variant,
|
||||
) -> bool {
|
||||
match num {
|
||||
0 => {
|
||||
// Проверяем, является ли первый параметр строкой или бинарными данными
|
||||
let message: Vec<u8> = match params.get(0).map(|p| p.get()) {
|
||||
Some(ParamValue::Str(str_message)) => {
|
||||
// Преобразуем строку из &[u16] в Vec<u8>
|
||||
String::from_utf16(str_message)
|
||||
.unwrap_or_default()
|
||||
.into_bytes()
|
||||
},
|
||||
Some(ParamValue::Blob(blob_message)) => {
|
||||
// Просто передаем бинарные данные
|
||||
blob_message.to_vec()
|
||||
},
|
||||
_ => return false, // Неверный тип данных
|
||||
};
|
||||
|
||||
// Извлекаем таймаут из второго параметра
|
||||
let ParamValue::I32(timeout) = params.get(1).map(|p| p.get()).unwrap_or(ParamValue::I32(0)) else {
|
||||
return false; // Неверный тип для таймаута
|
||||
};
|
||||
|
||||
// Вызываем send_message с address, port, извлечённым message и timeout
|
||||
send_message(&self.address, self.port, &message, timeout as u64, self.ssl, ret_value)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_utf16(input: &str) -> Vec<u16> {
|
||||
input.encode_utf16().collect()
|
||||
}
|
||||
|
||||
pub fn send_message(
|
||||
address: &str,
|
||||
port: i32,
|
||||
message: &dyn AsRef<[u8]>,
|
||||
timeout_secs: u64,
|
||||
use_ssl: bool,
|
||||
ret_value: &mut Variant,
|
||||
) -> bool {
|
||||
let addr = format!("{}:{}", address, port);
|
||||
|
||||
// Устанавливаем таймаут
|
||||
let timeout = Duration::from_secs(timeout_secs);
|
||||
|
||||
// Создаем TCP соединение
|
||||
let stream = TcpStream::connect_timeout(&addr.to_socket_addrs().unwrap().next().unwrap(), timeout);
|
||||
let stream = match stream {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to connect: {}", e)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Применяем таймауты к потокам чтения и записи
|
||||
if let Err(e) = stream.set_read_timeout(Some(timeout)) {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to set read timeout: {}", e)));
|
||||
return true;
|
||||
}
|
||||
if let Err(e) = stream.set_write_timeout(Some(timeout)) {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to set write timeout: {}", e)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Обрабатываем SSL при необходимости
|
||||
let mut stream = if use_ssl {
|
||||
let connector = match TlsConnector::new() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to create TLS connector: {}", e)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
match connector.connect(address, stream) {
|
||||
Ok(s) => StreamWrapper::Secure(s),
|
||||
Err(e) => {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to establish SSL connection: {}", e)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
StreamWrapper::Plain(stream)
|
||||
};
|
||||
|
||||
// Отправляем сообщение
|
||||
if let Err(e) = stream.write_all(message.as_ref()) {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to send message: {}", e)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Читаем ответ
|
||||
let mut response = Vec::new();
|
||||
match stream.read_to_end(&mut response) {
|
||||
Ok(_) => {
|
||||
match String::from_utf8(response) {
|
||||
Ok(s) => {
|
||||
let _ = ret_value.set_str(&to_utf16(&s));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = ret_value.set_str(&to_utf16(&format!("Failed to decode response: {}", e)));
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
ret_value.set_str(&to_utf16(&format!("Failed to read response: {}", e)));
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum StreamWrapper {
|
||||
Plain(TcpStream),
|
||||
Secure(TlsStream<TcpStream>),
|
||||
}
|
||||
|
||||
impl Read for StreamWrapper {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
StreamWrapper::Plain(stream) => stream.read(buf),
|
||||
StreamWrapper::Secure(stream) => stream.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for StreamWrapper {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
StreamWrapper::Plain(stream) => stream.write(buf),
|
||||
StreamWrapper::Secure(stream) => stream.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self {
|
||||
StreamWrapper::Plain(stream) => stream.flush(),
|
||||
StreamWrapper::Secure(stream) => stream.flush(),
|
||||
}
|
||||
}
|
||||
}
|
2
src/addins/tmpl/release.bat
Normal file
2
src/addins/tmpl/release.bat
Normal file
@ -0,0 +1,2 @@
|
||||
cargo build --release --target x86_64-pc-windows-msvc
|
||||
cargo build --release --target x86_64-unknown-linux-gnu
|
Loading…
Reference in New Issue
Block a user