On reformate le code ;-)
This commit is contained in:
245
Cargo.lock
generated
245
Cargo.lock
generated
@@ -54,6 +54,15 @@ version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
@@ -300,6 +309,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
@@ -310,6 +328,52 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -364,6 +428,17 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -770,6 +845,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -799,6 +880,8 @@ checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -850,6 +933,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
@@ -1052,7 +1144,10 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"uuid",
|
||||
"validator",
|
||||
"xmltree",
|
||||
]
|
||||
|
||||
@@ -1072,6 +1167,28 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
@@ -1116,6 +1233,18 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.11"
|
||||
@@ -1314,6 +1443,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
@@ -1342,6 +1477,12 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
@@ -1595,6 +1736,48 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-swagger-ui"
|
||||
version = "9.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"utoipa",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
@@ -1606,6 +1789,36 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
"validator_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"once_cell",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
@@ -1980,3 +2193,35 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
@@ -31,3 +31,6 @@ axum-server = "0.7.2"
|
||||
axum-embed = "0.1.0"
|
||||
rust-embed = "8.7.2"
|
||||
anyhow = "1.0"
|
||||
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
|
||||
validator = { version = "0.20.0", features = ["derive"] }
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
mod object_trait;
|
||||
|
||||
pub mod variable_types;
|
||||
pub mod server;
|
||||
pub mod state_variables;
|
||||
pub mod value_ranges;
|
||||
pub mod server;
|
||||
pub mod variable_types;
|
||||
|
||||
pub use crate::object_trait::UpnpObject;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UpnpObjectType {
|
||||
name: String,
|
||||
|
||||
@@ -2,7 +2,6 @@ use xmltree::{Element, EmitterConfig};
|
||||
|
||||
use crate::UpnpObjectType;
|
||||
|
||||
|
||||
pub trait UpnpObject {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
||||
fn to_xml_element(&self) -> Element;
|
||||
@@ -35,5 +34,4 @@ pub trait UpnpObject {
|
||||
|
||||
xml_string
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,12 @@ use std::{
|
||||
};
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
response::{sse::{Event, KeepAlive, Sse}, IntoResponse},
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
response::{
|
||||
IntoResponse,
|
||||
sse::{Event, KeepAlive, Sse},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::broadcast;
|
||||
@@ -59,8 +62,6 @@ impl LogState {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Query params pour /log-sse
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LogQuery {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use tracing::field::{Field, Visit};
|
||||
use tracing::{Event, Subscriber};
|
||||
use tracing_subscriber::{layer::Context, Layer};
|
||||
use tracing::field::{Visit, Field};
|
||||
use tracing_subscriber::{Layer, layer::Context};
|
||||
|
||||
use super::{LogEntry, LogState};
|
||||
use std::time::SystemTime;
|
||||
@@ -26,7 +26,8 @@ impl Visit for LogVisitor {
|
||||
if !self.message.is_empty() {
|
||||
self.message.push(' ');
|
||||
}
|
||||
self.message.push_str(&format!("{}={:?}", field.name(), value));
|
||||
self.message
|
||||
.push_str(&format!("{}={:?}", field.name(), value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,38 +10,8 @@
|
||||
//! - ⚛️ **Applications SPA** : Support pour Vue.js/React avec `add_spa()`
|
||||
//! - 🔀 **Redirections** : Redirigez des routes avec `add_redirect()`
|
||||
//! - 🎯 **Handlers personnalisés** : Support SSE, WebSocket, etc. avec `add_handler_with_state()`
|
||||
//! - 📚 **Documentation API** : OpenAPI/Swagger automatique avec `add_openapi()`
|
||||
//! - ⚡ **Gestion gracieuse** : Arrêt propre sur Ctrl+C
|
||||
//!
|
||||
//! ## Exemple d'utilisation
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use pmoupnp::server::{ServerBuilder, Server};
|
||||
//! use rust_embed::RustEmbed;
|
||||
//!
|
||||
//! #[derive(RustEmbed, Clone)]
|
||||
//! #[folder = "static/"]
|
||||
//! struct Assets;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() {
|
||||
//! let mut server = ServerBuilder::new("MyAPI", "http://localhost:3000", 3000)
|
||||
//! .build();
|
||||
//!
|
||||
//! // Route JSON simple
|
||||
//! server.add_route("/api/hello", || async {
|
||||
//! serde_json::json!({"message": "Hello World"})
|
||||
//! }).await;
|
||||
//!
|
||||
//! // Redirection
|
||||
//! server.add_redirect("/", "/app").await;
|
||||
//!
|
||||
//! // Application Vue.js
|
||||
//! server.add_spa::<Assets>("/app").await;
|
||||
//!
|
||||
//! server.start().await;
|
||||
//! server.wait().await;
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub mod logs;
|
||||
|
||||
@@ -55,13 +25,18 @@ use rust_embed::RustEmbed;
|
||||
use serde::Serialize;
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use tokio::{signal, sync::RwLock, task::JoinHandle};
|
||||
use tracing::{info,warn,debug,error};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
/// Info serveur sérialisable
|
||||
#[derive(Clone, Serialize)]
|
||||
#[derive(Clone, Serialize, utoipa::ToSchema)]
|
||||
pub struct ServerInfo {
|
||||
/// Nom du serveur
|
||||
pub name: String,
|
||||
/// URL de base
|
||||
pub base_url: String,
|
||||
/// Port HTTP
|
||||
pub http_port: u16,
|
||||
}
|
||||
|
||||
@@ -71,6 +46,7 @@ pub struct Server {
|
||||
base_url: String,
|
||||
http_port: u16,
|
||||
router: Arc<RwLock<Router>>,
|
||||
api_router: Arc<RwLock<Option<Router>>>,
|
||||
join_handle: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
@@ -99,6 +75,7 @@ impl Server {
|
||||
base_url: base_url.into(),
|
||||
http_port,
|
||||
router: Arc::new(RwLock::new(Router::new())),
|
||||
api_router: Arc::new(RwLock::new(None)),
|
||||
join_handle: None,
|
||||
}
|
||||
}
|
||||
@@ -111,7 +88,6 @@ impl Server {
|
||||
return Self::new("PMO-Music-Server", url, port);
|
||||
}
|
||||
|
||||
/// Ajoute une route dynamique
|
||||
/// Ajoute une route JSON dynamique
|
||||
///
|
||||
/// Crée un endpoint qui retourne du JSON. La closure fournie sera appelée
|
||||
@@ -343,9 +319,7 @@ impl Server {
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
let route = Router::new()
|
||||
.route("/", get(handler))
|
||||
.with_state(state);
|
||||
let route = Router::new().route("/", get(handler)).with_state(state);
|
||||
|
||||
let mut r = self.router.write().await;
|
||||
*r = std::mem::take(&mut *r).nest(path, route);
|
||||
@@ -413,6 +387,54 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ajoute une API documentée avec OpenAPI
|
||||
///
|
||||
/// Monte un routeur d'API sous `/api` et active Swagger UI sur `/swagger-ui`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `api_router` - Router Axum contenant les routes API
|
||||
/// * `openapi` - Spécification OpenAPI générée par utoipa
|
||||
///
|
||||
/// # Exemple
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use utoipa::OpenApi;
|
||||
/// use axum::{Router, Json, routing::get};
|
||||
///
|
||||
/// #[derive(utoipa::OpenApi)]
|
||||
/// #[openapi(
|
||||
/// paths(get_users),
|
||||
/// components(schemas(User))
|
||||
/// )]
|
||||
/// struct ApiDoc;
|
||||
///
|
||||
/// #[utoipa::path(
|
||||
/// get,
|
||||
/// path = "/users",
|
||||
/// responses((status = 200, description = "List users"))
|
||||
/// )]
|
||||
/// async fn get_users() -> Json<Vec<User>> {
|
||||
/// Json(vec![])
|
||||
/// }
|
||||
///
|
||||
/// let api_router = Router::new()
|
||||
/// .route("/users", get(get_users));
|
||||
///
|
||||
/// server.add_openapi(api_router, ApiDoc::openapi()).await;
|
||||
/// ```
|
||||
pub async fn add_openapi(&mut self, api_router: Router, openapi: utoipa::openapi::OpenApi) {
|
||||
// Stocker le routeur API
|
||||
let mut api_r = self.api_router.write().await;
|
||||
*api_r = Some(api_router);
|
||||
|
||||
// Ajouter Swagger UI
|
||||
let swagger = SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi);
|
||||
|
||||
let mut r = self.router.write().await;
|
||||
*r = std::mem::take(&mut *r).merge(swagger);
|
||||
}
|
||||
|
||||
/// Démarre le serveur HTTP
|
||||
///
|
||||
/// Lance le serveur sur le port configuré et met en place la gestion
|
||||
@@ -431,7 +453,18 @@ impl Server {
|
||||
/// ```
|
||||
pub async fn start(&mut self) {
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], self.http_port));
|
||||
info!("Server {} running at [http://{}:{}](http://{}:{})", self.name, self.base_url, self.http_port, self.base_url, self.http_port);
|
||||
info!(
|
||||
"Server {} running at [http://{}:{}](http://{}:{})",
|
||||
self.name, self.base_url, self.http_port, self.base_url, self.http_port
|
||||
);
|
||||
|
||||
// Merger le routeur API si présent
|
||||
let api_router = self.api_router.read().await;
|
||||
if let Some(api_r) = api_router.as_ref() {
|
||||
let mut r = self.router.write().await;
|
||||
*r = std::mem::take(&mut *r).nest("/api", api_r.clone());
|
||||
}
|
||||
drop(api_router);
|
||||
|
||||
let router = self.router.clone();
|
||||
|
||||
@@ -499,7 +532,7 @@ impl ServerBuilder {
|
||||
Self {
|
||||
name: "PMO-Music-Server".to_string(),
|
||||
base_url: config.get_base_url(),
|
||||
http_port: config.get_http_port()
|
||||
http_port: config.get_http_port(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ use chrono::{DateTime, Utc};
|
||||
use xmltree::Element;
|
||||
|
||||
use crate::{
|
||||
UpnpObject, UpnpObjectType,
|
||||
state_variables::{StateVarInstance, StateVariable, UpnpVariable},
|
||||
variable_types::StateValue,
|
||||
UpnpObject,
|
||||
UpnpObjectType
|
||||
};
|
||||
|
||||
impl UpnpVariable for StateVarInstance {
|
||||
@@ -35,7 +34,7 @@ impl StateVarInstance {
|
||||
value: from.get_default(),
|
||||
old_value: from.get_default(),
|
||||
last_modified: Utc::now(),
|
||||
last_notification: Utc::now()
|
||||
last_notification: Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,5 +53,4 @@ impl StateVarInstance {
|
||||
pub fn last_modified(&self) -> DateTime<Utc> {
|
||||
self.last_modified
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
mod errors;
|
||||
mod variable_trait;
|
||||
mod variable_methods;
|
||||
mod instance_methods;
|
||||
mod variable_methods;
|
||||
mod variable_trait;
|
||||
|
||||
use std::{collections::HashMap, sync::{Arc, RwLock}};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
pub use errors::{StateVariableError};
|
||||
pub use crate::state_variables::variable_trait::UpnpVariable;
|
||||
use chrono::{DateTime, Utc};
|
||||
pub use errors::StateVariableError;
|
||||
|
||||
|
||||
use crate::{value_ranges::ValueRange, variable_types::{StateValue, StateVarType}, UpnpObjectType};
|
||||
use crate::{
|
||||
UpnpObjectType,
|
||||
value_ranges::ValueRange,
|
||||
variable_types::{StateValue, StateVarType},
|
||||
};
|
||||
|
||||
/// Type pour les fonctions de condition d'événement
|
||||
pub type StateConditionFunc = Arc<dyn Fn(&StateVarInstance) -> bool + Send + Sync>;
|
||||
|
||||
/// Type pour les fonctions de parsing de valeurs depuis des chaînes
|
||||
pub type StringValueParser = Arc<dyn Fn(&str) -> Result<StateValue, StateVariableError> + Send + Sync>;
|
||||
pub type StringValueParser =
|
||||
Arc<dyn Fn(&str) -> Result<StateValue, StateVariableError> + Send + Sync>;
|
||||
|
||||
/// Type pour les fonctions de sérialisation de valeurs vers des chaînes
|
||||
pub type ValueSerializer = Arc<dyn Fn(&StateValue) -> Result<String, StateVariableError> + Send + Sync>;
|
||||
|
||||
pub type ValueSerializer =
|
||||
Arc<dyn Fn(&StateValue) -> Result<String, StateVariableError> + Send + Sync>;
|
||||
|
||||
pub struct StateVariable {
|
||||
object: UpnpObjectType,
|
||||
|
||||
@@ -65,11 +65,15 @@ fn to_xml_element(&self) -> Element {
|
||||
let mut range_elem = Element::new("allowedValueRange");
|
||||
|
||||
let mut min_elem = Element::new("minimum");
|
||||
min_elem.children.push(XMLNode::Text(range.get_minimum().to_string()));
|
||||
min_elem
|
||||
.children
|
||||
.push(XMLNode::Text(range.get_minimum().to_string()));
|
||||
range_elem.children.push(XMLNode::Element(min_elem));
|
||||
|
||||
let mut max_elem = Element::new("maximum");
|
||||
max_elem.children.push(XMLNode::Text(range.get_maximum().to_string()));
|
||||
max_elem
|
||||
.children
|
||||
.push(XMLNode::Text(range.get_maximum().to_string()));
|
||||
range_elem.children.push(XMLNode::Element(max_elem));
|
||||
|
||||
if let Some(step) = &self.step {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod methods;
|
||||
|
||||
|
||||
use crate::variable_types::StateValue;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::variable_types::{StateValue, StateVarType, StateValueError};
|
||||
use crate::variable_types::{StateValue, StateValueError, StateVarType};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl StateValue {
|
||||
pub fn try_cast(&self, target: StateVarType) -> Result<StateValue, StateValueError> {
|
||||
|
||||
let source = StateVarType::from(self);
|
||||
|
||||
// Identité (même type)
|
||||
|
||||
@@ -24,18 +24,20 @@ impl StateVarType {
|
||||
StateVarType::BinBase64 => StateValue::BinBase64(String::new()),
|
||||
StateVarType::BinHex => StateValue::BinHex(String::new()),
|
||||
StateVarType::Date => StateValue::Date(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()),
|
||||
StateVarType::DateTime => StateValue::DateTime(DateTime::from_timestamp(0, 0).unwrap().naive_utc().into()),
|
||||
StateVarType::DateTimeTZ => StateValue::DateTimeTZ(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
StateVarType::DateTime => {
|
||||
StateValue::DateTime(DateTime::from_timestamp(0, 0).unwrap().naive_utc().into())
|
||||
}
|
||||
StateVarType::DateTimeTZ => {
|
||||
StateValue::DateTimeTZ(DateTime::from_naive_utc_and_offset(
|
||||
DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
|
||||
FixedOffset::east_opt(0).unwrap())
|
||||
),
|
||||
FixedOffset::east_opt(0).unwrap(),
|
||||
))
|
||||
}
|
||||
StateVarType::Time => StateValue::Time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()),
|
||||
StateVarType::TimeTZ => StateValue::TimeTZ(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
StateVarType::TimeTZ => StateValue::TimeTZ(DateTime::from_naive_utc_and_offset(
|
||||
DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
|
||||
FixedOffset::east_opt(0).unwrap())
|
||||
),
|
||||
FixedOffset::east_opt(0).unwrap(),
|
||||
)),
|
||||
StateVarType::UUID => StateValue::UUID(Uuid::nil()),
|
||||
StateVarType::URI => StateValue::URI(Url::parse("http://localhost").unwrap()),
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::fmt;
|
||||
|
||||
use crate::variable_types::StateVarType;
|
||||
|
||||
|
||||
impl fmt::Display for StateVarType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = match self {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::fmt;
|
||||
use base64::engine::general_purpose;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose;
|
||||
use std::fmt;
|
||||
|
||||
use crate::variable_types::StateValue;
|
||||
|
||||
|
||||
impl fmt::Display for StateValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
use crate::variable_types::StateVarType;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromStr for StateVarType {
|
||||
type Err = String; // Type d'erreur personnalisé
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
mod type_methods;
|
||||
mod value_methods;
|
||||
mod type_trait;
|
||||
mod value_trait;
|
||||
mod fromstr;
|
||||
mod cast;
|
||||
mod default_value;
|
||||
mod display_type;
|
||||
mod display_value;
|
||||
mod default_value;
|
||||
mod errors;
|
||||
mod fromstr;
|
||||
mod type_methods;
|
||||
mod type_trait;
|
||||
mod value_methods;
|
||||
mod value_trait;
|
||||
|
||||
mod values_from_type;
|
||||
|
||||
mod values_from_u8;
|
||||
mod values_from_u16;
|
||||
mod values_from_u32;
|
||||
mod values_from_i8;
|
||||
mod values_from_i16;
|
||||
mod values_from_i32;
|
||||
mod values_from_i64;
|
||||
mod values_from_i8;
|
||||
mod values_from_u16;
|
||||
mod values_from_u32;
|
||||
mod values_from_u8;
|
||||
|
||||
mod values_from_f32;
|
||||
mod values_from_f64;
|
||||
|
||||
mod values_from_uri;
|
||||
mod values_from_uuid;
|
||||
mod values_from_datetime;
|
||||
mod values_from_naivedate;
|
||||
mod values_from_naivedatetime;
|
||||
mod values_from_naivetime;
|
||||
mod values_from_datetime;
|
||||
mod values_from_uri;
|
||||
mod values_from_uuid;
|
||||
mod values_from_vec_u8;
|
||||
|
||||
use std::fmt::{Debug};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use url::Url;
|
||||
@@ -92,5 +92,3 @@ pub enum StateValue {
|
||||
UUID(Uuid),
|
||||
URI(Url),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::variable_types::{type_trait::UpnpVarType, StateVarType};
|
||||
use crate::variable_types::{StateVarType, type_trait::UpnpVarType};
|
||||
|
||||
impl UpnpVarType for StateVarType {
|
||||
fn as_state_var_type(&self) -> StateVarType {
|
||||
@@ -62,10 +62,7 @@ impl UpnpVarType for StateVarType {
|
||||
fn is_float(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
StateVarType::R4
|
||||
| StateVarType::R8
|
||||
| StateVarType::Number
|
||||
| StateVarType::Fixed14_4
|
||||
StateVarType::R4 | StateVarType::R8 | StateVarType::Number | StateVarType::Fixed14_4
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,10 +71,13 @@ impl UpnpVarType for StateVarType {
|
||||
}
|
||||
|
||||
fn is_string(&self) -> bool {
|
||||
matches!(self, StateVarType::String
|
||||
matches!(
|
||||
self,
|
||||
StateVarType::String
|
||||
| StateVarType::Char
|
||||
| StateVarType::BinHex
|
||||
| StateVarType::BinBase64)
|
||||
| StateVarType::BinBase64
|
||||
)
|
||||
}
|
||||
|
||||
fn is_time(&self) -> bool {
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::cmp::Ordering;
|
||||
|
||||
use crate::variable_types::{StateValue, StateVarType, type_trait::UpnpVarType};
|
||||
|
||||
|
||||
impl UpnpVarType for StateValue {
|
||||
fn as_state_var_type(&self) -> StateVarType {
|
||||
StateVarType::from(self) // utilise ton From<&StateValue> existant
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
pub trait UpnpValue {
|
||||
}
|
||||
pub trait UpnpValue {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use chrono::{DateTime,FixedOffset};
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for DateTime<FixedOffset> {
|
||||
type Error = StateValueError;
|
||||
@@ -9,9 +9,15 @@ impl TryFrom<&StateValue> for DateTime<FixedOffset> {
|
||||
match value {
|
||||
StateValue::DateTimeTZ(v) => Ok(v.clone()),
|
||||
StateValue::TimeTZ(v) => Ok(v.clone()),
|
||||
StateValue::String(v) => DateTime::parse_from_rfc3339(v)
|
||||
.map_err(|e| StateValueError::ParseError(format!("Cannot parse DateTimeTZ from string '{}': {}", v, e))),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to DateTime<FixedOffset>".into())),
|
||||
StateValue::String(v) => DateTime::parse_from_rfc3339(v).map_err(|e| {
|
||||
StateValueError::ParseError(format!(
|
||||
"Cannot parse DateTimeTZ from string '{}': {}",
|
||||
v, e
|
||||
))
|
||||
}),
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to DateTime<FixedOffset>".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,9 +31,7 @@ impl TryFrom<StateValue> for DateTime<FixedOffset> {
|
||||
}
|
||||
|
||||
impl From<DateTime<FixedOffset>> for StateValue {
|
||||
|
||||
fn from(value: DateTime<FixedOffset>) -> Self {
|
||||
StateValue::DateTimeTZ(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::convert::TryFrom;
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
|
||||
impl TryFrom<&StateValue> for f32 {
|
||||
type Error = StateValueError;
|
||||
|
||||
@@ -12,12 +11,8 @@ impl TryFrom<&StateValue> for f32 {
|
||||
// --- Signed integers ---
|
||||
StateValue::I1(v) => Ok(*v as f32),
|
||||
StateValue::I2(v) => Ok(*v as f32),
|
||||
StateValue::I4(v)
|
||||
if *v > -MAX_EXACT &&
|
||||
*v < MAX_EXACT => Ok(*v as f32),
|
||||
StateValue::Int(v)
|
||||
if *v >= -MAX_EXACT &&
|
||||
*v <= MAX_EXACT as i32 => Ok(*v as f32),
|
||||
StateValue::I4(v) if *v > -MAX_EXACT && *v < MAX_EXACT => Ok(*v as f32),
|
||||
StateValue::Int(v) if *v >= -MAX_EXACT && *v <= MAX_EXACT as i32 => Ok(*v as f32),
|
||||
|
||||
// --- Unsigned integers ---
|
||||
StateValue::UI1(v) => Ok(*v as f32),
|
||||
@@ -30,37 +25,38 @@ impl TryFrom<&StateValue> for f32 {
|
||||
// --- Floats ---
|
||||
StateValue::R4(v) => Ok(*v), // déjà un f32
|
||||
StateValue::R8(v)
|
||||
if ! v.is_finite() ||
|
||||
(*v <= f32::MAX as f64 &&
|
||||
*v >= f32::MIN as f64) => Ok(*v as f32),
|
||||
if !v.is_finite() || (*v <= f32::MAX as f64 && *v >= f32::MIN as f64) =>
|
||||
{
|
||||
Ok(*v as f32)
|
||||
}
|
||||
StateValue::R8(_) => Err(StateValueError::TypeError(
|
||||
"Cannot cast R8 to f32: out of range".into(),
|
||||
)),
|
||||
StateValue::Number(v)
|
||||
if ! v.is_finite() ||
|
||||
(*v <= f32::MAX as f64 &&
|
||||
*v >= f32::MIN as f64) => Ok(*v as f32),
|
||||
if !v.is_finite() || (*v <= f32::MAX as f64 && *v >= f32::MIN as f64) =>
|
||||
{
|
||||
Ok(*v as f32)
|
||||
}
|
||||
StateValue::Number(_) => Err(StateValueError::TypeError(
|
||||
"Cannot cast Number to f32: out of range".into(),
|
||||
)),
|
||||
StateValue::Fixed14_4(v)
|
||||
if ! v.is_finite() ||
|
||||
(*v <= f32::MAX as f64 &&
|
||||
*v >= f32::MIN as f64) => Ok(*v as f32),
|
||||
if !v.is_finite() || (*v <= f32::MAX as f64 && *v >= f32::MIN as f64) =>
|
||||
{
|
||||
Ok(*v as f32)
|
||||
}
|
||||
StateValue::Fixed14_4(_) => Err(StateValueError::TypeError(
|
||||
"Cannot cast Fixed14_4 to f32: out of range".into(),
|
||||
)),
|
||||
|
||||
StateValue::Boolean(v) => Ok((*v as i32) as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<f32>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as f32", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<f32>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as f32", s))),
|
||||
|
||||
// --- Par défaut : erreur ---
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to f32".into(),
|
||||
)),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to f32".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,9 +70,7 @@ impl TryFrom<StateValue> for f32 {
|
||||
}
|
||||
|
||||
impl From<f32> for StateValue {
|
||||
|
||||
fn from(value: f32) -> Self {
|
||||
StateValue::R4(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,12 @@ impl TryFrom<&StateValue> for f64 {
|
||||
|
||||
StateValue::Boolean(v) => Ok((*v as i32) as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<f64>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as f64", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<f64>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as f64", s))),
|
||||
|
||||
// --- Par défaut : erreur ---
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to f64".into(),
|
||||
)),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to f64".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
// Implémentations TryFrom<StateValue> pour types numériques
|
||||
@@ -20,9 +19,9 @@ impl TryFrom<&StateValue> for i16 {
|
||||
StateValue::UI4(v) if *v <= i16::MAX as u32 => Ok(*v as i16),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<i16>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as i16", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<i16>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as i16", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to i32".into())),
|
||||
}
|
||||
@@ -38,9 +37,7 @@ impl TryFrom<StateValue> for i16 {
|
||||
}
|
||||
|
||||
impl From<i16> for StateValue {
|
||||
|
||||
fn from(value: i16) -> Self {
|
||||
StateValue::I2(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::convert::TryFrom;
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
|
||||
impl TryFrom<&StateValue> for i32 {
|
||||
type Error = StateValueError;
|
||||
|
||||
@@ -20,9 +19,9 @@ impl TryFrom<&StateValue> for i32 {
|
||||
StateValue::UI4(v) if *v <= i32::MAX as u32 => Ok(*v as i32),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<i32>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as i32", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<i32>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as i32", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to i32".into())),
|
||||
}
|
||||
@@ -38,9 +37,7 @@ impl TryFrom<StateValue> for i32 {
|
||||
}
|
||||
|
||||
impl From<i32> for StateValue {
|
||||
|
||||
fn from(value: i32) -> Self {
|
||||
StateValue::I4(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::convert::TryFrom;
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for i64 {
|
||||
type Error = StateValueError;
|
||||
@@ -21,9 +21,9 @@ impl TryFrom<&StateValue> for i64 {
|
||||
StateValue::Boolean(v) => Ok(*v as i64),
|
||||
|
||||
// chaîne → i64
|
||||
StateValue::String(s) => s.parse::<i64>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as i64", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<i64>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as i64", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to i64".into())),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
// Implémentations TryFrom<StateValue> pour types numériques
|
||||
@@ -20,9 +19,9 @@ impl TryFrom<&StateValue> for i8 {
|
||||
StateValue::UI4(v) if *v <= i8::MAX as u32 => Ok(*v as i8),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<i8>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as i8", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<i8>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as i8", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to i8".into())),
|
||||
}
|
||||
@@ -38,9 +37,7 @@ impl TryFrom<StateValue> for i8 {
|
||||
}
|
||||
|
||||
impl From<i8> for StateValue {
|
||||
|
||||
fn from(value: i8) -> Self {
|
||||
StateValue::I1(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use chrono::NaiveDate;
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use chrono::NaiveDate;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for NaiveDate {
|
||||
type Error = StateValueError;
|
||||
@@ -8,9 +8,12 @@ impl TryFrom<&StateValue> for NaiveDate {
|
||||
fn try_from(value: &StateValue) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
StateValue::Date(v) => Ok(v.clone()),
|
||||
StateValue::String(v) => NaiveDate::parse_from_str(v, "%Y-%m-%d")
|
||||
.map_err(|e| StateValueError::ParseError(format!("Cannot parse Date from string '{}': {}", v, e))),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to NaiveDate".into())),
|
||||
StateValue::String(v) => NaiveDate::parse_from_str(v, "%Y-%m-%d").map_err(|e| {
|
||||
StateValueError::ParseError(format!("Cannot parse Date from string '{}': {}", v, e))
|
||||
}),
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to NaiveDate".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +27,7 @@ impl TryFrom<StateValue> for NaiveDate {
|
||||
}
|
||||
|
||||
impl From<NaiveDate> for StateValue {
|
||||
|
||||
fn from(value: NaiveDate) -> Self {
|
||||
StateValue::Date(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use chrono::NaiveDateTime;
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use chrono::NaiveDateTime;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for NaiveDateTime {
|
||||
type Error = StateValueError;
|
||||
@@ -9,8 +9,15 @@ impl TryFrom<&StateValue> for NaiveDateTime {
|
||||
match value {
|
||||
StateValue::DateTime(v) => Ok(v.clone()),
|
||||
StateValue::String(v) => NaiveDateTime::parse_from_str(&v, "%Y-%m-%dT%H:%M:%S")
|
||||
.map_err(|e| StateValueError::ParseError(format!("Cannot parse DateTime from string '{}': {}", v, e))),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to NaiveDateTime".into())),
|
||||
.map_err(|e| {
|
||||
StateValueError::ParseError(format!(
|
||||
"Cannot parse DateTime from string '{}': {}",
|
||||
v, e
|
||||
))
|
||||
}),
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to NaiveDateTime".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +31,6 @@ impl TryFrom<StateValue> for NaiveDateTime {
|
||||
}
|
||||
|
||||
impl From<NaiveDateTime> for StateValue {
|
||||
|
||||
fn from(value: NaiveDateTime) -> Self {
|
||||
StateValue::DateTime(value)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use chrono::NaiveTime;
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use chrono::NaiveTime;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for NaiveTime {
|
||||
type Error = StateValueError;
|
||||
@@ -8,9 +8,12 @@ impl TryFrom<&StateValue> for NaiveTime {
|
||||
fn try_from(value: &StateValue) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
StateValue::Time(v) => Ok(v.clone()),
|
||||
StateValue::String(v) => NaiveTime::parse_from_str(&v, "%H:%M:%S")
|
||||
.map_err(|e| StateValueError::ParseError(format!("Cannot parse Time from string '{}': {}", v, e))),
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to NaiveTime".into())),
|
||||
StateValue::String(v) => NaiveTime::parse_from_str(&v, "%H:%M:%S").map_err(|e| {
|
||||
StateValueError::ParseError(format!("Cannot parse Time from string '{}': {}", v, e))
|
||||
}),
|
||||
_ => Err(StateValueError::TypeError(
|
||||
"Cannot cast to NaiveTime".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +27,6 @@ impl TryFrom<StateValue> for NaiveTime {
|
||||
}
|
||||
|
||||
impl From<NaiveTime> for StateValue {
|
||||
|
||||
fn from(value: NaiveTime) -> Self {
|
||||
StateValue::Time(value)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
// Implémentations TryFrom<StateValue> pour types numériques
|
||||
@@ -20,9 +18,9 @@ impl TryFrom<&StateValue> for u16 {
|
||||
StateValue::Int(v) if *v >= 0 && *v <= u16::MAX as i32 => Ok(*v as Self),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<u16>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as u16", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<u16>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as u16", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to u16".into())),
|
||||
}
|
||||
@@ -37,11 +35,8 @@ impl TryFrom<StateValue> for u16 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<u16> for StateValue {
|
||||
|
||||
fn from(value: u16) -> Self {
|
||||
StateValue::UI2(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
impl TryFrom<&StateValue> for u32 {
|
||||
@@ -17,9 +16,9 @@ impl TryFrom<&StateValue> for u32 {
|
||||
StateValue::Int(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<u32>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as u32", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<u32>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as u32", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to u32".into())),
|
||||
}
|
||||
@@ -34,12 +33,8 @@ impl TryFrom<StateValue> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl From<u32> for StateValue {
|
||||
|
||||
fn from(value: u32) -> Self {
|
||||
StateValue::UI4(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
|
||||
// Implémentations TryFrom<StateValue> pour types numériques
|
||||
@@ -19,9 +18,9 @@ impl TryFrom<&StateValue> for u8 {
|
||||
StateValue::Int(v) if *v >= 0 && *v <= u8::MAX as i32 => Ok(*v as u8),
|
||||
StateValue::Boolean(v) => Ok(*v as Self),
|
||||
|
||||
StateValue::String(s) => s.parse::<u8>().map_err(|_| {
|
||||
StateValueError::TypeError(format!("Cannot parse '{}' as u8", s))
|
||||
}),
|
||||
StateValue::String(s) => s
|
||||
.parse::<u8>()
|
||||
.map_err(|_| StateValueError::TypeError(format!("Cannot parse '{}' as u8", s))),
|
||||
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to u8".into())),
|
||||
}
|
||||
@@ -37,9 +36,7 @@ impl TryFrom<StateValue> for u8 {
|
||||
}
|
||||
|
||||
impl From<u8> for StateValue {
|
||||
|
||||
fn from(value: u8) -> Self {
|
||||
StateValue::UI1(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ impl TryFrom<StateValue> for Url {
|
||||
|
||||
// Si c'est une String, on tente un parse
|
||||
StateValue::String(v) => {
|
||||
Url::parse(&v)
|
||||
.map_err(|_| StateValueError::TypeError("Invalid URI string".into()))
|
||||
Url::parse(&v).map_err(|_| StateValueError::TypeError("Invalid URI string".into()))
|
||||
}
|
||||
|
||||
// Autres types : erreur
|
||||
@@ -24,7 +23,6 @@ impl TryFrom<StateValue> for Url {
|
||||
}
|
||||
|
||||
impl From<Url> for StateValue {
|
||||
|
||||
fn from(value: Url) -> Self {
|
||||
StateValue::URI(value)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ impl TryFrom<StateValue> for Uuid {
|
||||
StateValue::UUID(v) => Ok(v),
|
||||
|
||||
// Si c'est une String, on tente un parse
|
||||
StateValue::String(v) => {
|
||||
Uuid::parse_str(&v)
|
||||
.map_err(|_| StateValueError::TypeError("Invalid UUID string".into()))
|
||||
}
|
||||
StateValue::String(v) => Uuid::parse_str(&v)
|
||||
.map_err(|_| StateValueError::TypeError("Invalid UUID string".into())),
|
||||
|
||||
// Autres types : erreur
|
||||
_ => Err(StateValueError::TypeError("Cannot cast to Uuid".into())),
|
||||
@@ -24,7 +22,6 @@ impl TryFrom<StateValue> for Uuid {
|
||||
}
|
||||
|
||||
impl From<Uuid> for StateValue {
|
||||
|
||||
fn from(value: Uuid) -> Self {
|
||||
StateValue::UUID(value)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use crate::variable_types::{StateValue, StateValueError};
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl TryFrom<&StateValue> for Vec<u8> {
|
||||
type Error = StateValueError;
|
||||
@@ -8,10 +8,11 @@ impl TryFrom<&StateValue> for Vec<u8> {
|
||||
fn try_from(value: &StateValue) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
// Déjà un vecteur binaire
|
||||
StateValue::BinBase64(v) => STANDARD.decode(v).map_err(
|
||||
|e| StateValueError::ParseError(format!("Base64 decode error: {}", e))),
|
||||
StateValue::BinHex(v) => hex::decode(v).map_err(
|
||||
|e| StateValueError::ParseError(format!("BinHex decode error: {}", e))),
|
||||
StateValue::BinBase64(v) => STANDARD
|
||||
.decode(v)
|
||||
.map_err(|e| StateValueError::ParseError(format!("Base64 decode error: {}", e))),
|
||||
StateValue::BinHex(v) => hex::decode(v)
|
||||
.map_err(|e| StateValueError::ParseError(format!("BinHex decode error: {}", e))),
|
||||
|
||||
// Conversion depuis une chaîne encodée
|
||||
StateValue::String(s) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::net::UdpSocket;
|
||||
use get_if_addrs::get_if_addrs;
|
||||
use std::net::UdpSocket;
|
||||
|
||||
pub fn guess_local_ip() -> String {
|
||||
// On tente de deviner l'IP locale
|
||||
@@ -17,7 +17,6 @@ pub fn guess_local_ip() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn list_all_ips() -> std::collections::HashMap<String, Vec<String>> {
|
||||
let mut result = std::collections::HashMap::new();
|
||||
|
||||
@@ -28,13 +27,17 @@ fn list_all_ips() -> std::collections::HashMap<String, Vec<String>> {
|
||||
continue;
|
||||
}
|
||||
if ip.is_ipv4() {
|
||||
result.entry(iface.name)
|
||||
result
|
||||
.entry(iface.name)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ip.to_string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.insert("error".to_string(), vec!["Failed to get interfaces".to_string()]);
|
||||
result.insert(
|
||||
"error".to_string(),
|
||||
vec!["Failed to get interfaces".to_string()],
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
Reference in New Issue
Block a user