On reformate le code ;-)
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
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,
|
||||
object_type: String,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@ use xmltree::{Element, EmitterConfig};
|
||||
|
||||
use crate::UpnpObjectType;
|
||||
|
||||
|
||||
pub trait UpnpObject {
|
||||
fn as_upnp_object_type(&self) -> &UpnpObjectType;
|
||||
fn to_xml_element(&self) -> Element;
|
||||
|
||||
fn get_name(&self)-> &String {
|
||||
fn get_name(&self) -> &String {
|
||||
return &self.as_upnp_object_type().name;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -85,7 +86,7 @@ pub async fn log_sse(
|
||||
Query(params): Query<LogQuery>,
|
||||
) -> impl IntoResponse {
|
||||
let mut rx = state.subscribe();
|
||||
|
||||
|
||||
// Récupérer l'historique du buffer
|
||||
let history = state.dump();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -193,9 +169,9 @@ impl Server {
|
||||
E: RustEmbed + Clone + Send + Sync + 'static,
|
||||
{
|
||||
let serve = ServeEmbed::<E>::new();
|
||||
|
||||
|
||||
let mut r = self.router.write().await;
|
||||
|
||||
|
||||
if path == "/" {
|
||||
*r = std::mem::take(&mut *r).fallback_service(serve);
|
||||
} else {
|
||||
@@ -253,9 +229,9 @@ impl Server {
|
||||
axum_embed::FallbackBehavior::Ok,
|
||||
Some("index.html".to_string()),
|
||||
);
|
||||
|
||||
|
||||
let mut r = self.router.write().await;
|
||||
|
||||
|
||||
if path == "/" {
|
||||
*r = std::mem::take(&mut *r).fallback_service(serve);
|
||||
} else {
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,25 @@ use thiserror::Error;
|
||||
pub enum StateVariableError {
|
||||
#[error("Conversion error: {0}")]
|
||||
ConversionError(String),
|
||||
|
||||
|
||||
#[error("Validation error: {0}")]
|
||||
ValidationError(String),
|
||||
|
||||
|
||||
#[error("Range error: {0}")]
|
||||
RangeError(String),
|
||||
|
||||
|
||||
#[error("Type error: {0}")]
|
||||
TypeError(String),
|
||||
|
||||
|
||||
#[error("Parse error: {0}")]
|
||||
ParseError(String),
|
||||
|
||||
|
||||
#[error("Event condition error: {0}")]
|
||||
EventConditionError(String),
|
||||
|
||||
|
||||
#[error("Arithmetic error: {0}")]
|
||||
ArithmeticError(String),
|
||||
|
||||
|
||||
#[error("Unknown error: {0}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ use chrono::{DateTime, Utc};
|
||||
use xmltree::Element;
|
||||
|
||||
use crate::{
|
||||
state_variables::{StateVarInstance, StateVariable, UpnpVariable},
|
||||
variable_types::StateValue,
|
||||
UpnpObject,
|
||||
UpnpObjectType
|
||||
UpnpObject, UpnpObjectType,
|
||||
state_variables::{StateVarInstance, StateVariable, UpnpVariable},
|
||||
variable_types::StateValue,
|
||||
};
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
pub use crate::state_variables::variable_trait::UpnpVariable;
|
||||
use chrono::{DateTime, Utc};
|
||||
pub use errors::{StateVariableError};
|
||||
pub use crate::state_variables::variable_trait::UpnpVariable;
|
||||
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,
|
||||
@@ -44,4 +51,4 @@ pub struct StateVarInstance {
|
||||
old_value: StateValue,
|
||||
last_modified: DateTime<Utc>,
|
||||
last_notification: DateTime<Utc>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl UpnpObject for StateVariable {
|
||||
return &self.object;
|
||||
}
|
||||
|
||||
fn to_xml_element(&self) -> Element {
|
||||
fn to_xml_element(&self) -> Element {
|
||||
// Création de l'élément racine <stateVariable>
|
||||
let mut root = Element::new("stateVariable");
|
||||
root.attributes.insert(
|
||||
@@ -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 {
|
||||
|
||||
@@ -49,7 +49,7 @@ pub trait UpnpVariable {
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.get_definition().as_state_var_type().default_value())
|
||||
}
|
||||
|
||||
|
||||
fn has_allowed_values(&self) -> bool {
|
||||
return self.get_definition().allowed_values.read().unwrap().len() > 0;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
mod methods;
|
||||
|
||||
|
||||
use crate::variable_types::StateValue;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueRange {
|
||||
min: StateValue,
|
||||
max: StateValue,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -14,18 +13,18 @@ impl StateValue {
|
||||
match (self, target) {
|
||||
(val, StateVarType::String) => Ok(StateValue::String(val.to_string())),
|
||||
|
||||
(_,StateVarType::UI1) => Ok(StateValue::UI1(u8::try_from(self)?)),
|
||||
(_,StateVarType::UI2) => Ok(StateValue::UI2(u16::try_from(self)?)),
|
||||
(_,StateVarType::UI4) => Ok(StateValue::UI4(u32::try_from(self)?)),
|
||||
(_,StateVarType::I1) => Ok(StateValue::I1(i8::try_from(self)?)),
|
||||
(_,StateVarType::I2) => Ok(StateValue::I2(i16::try_from(self)?)),
|
||||
(_,StateVarType::I4) => Ok(StateValue::I4(i32::try_from(self)?)),
|
||||
(_,StateVarType::Int) => Ok(StateValue::Int(i32::try_from(self)?)),
|
||||
(_, StateVarType::UI1) => Ok(StateValue::UI1(u8::try_from(self)?)),
|
||||
(_, StateVarType::UI2) => Ok(StateValue::UI2(u16::try_from(self)?)),
|
||||
(_, StateVarType::UI4) => Ok(StateValue::UI4(u32::try_from(self)?)),
|
||||
(_, StateVarType::I1) => Ok(StateValue::I1(i8::try_from(self)?)),
|
||||
(_, StateVarType::I2) => Ok(StateValue::I2(i16::try_from(self)?)),
|
||||
(_, StateVarType::I4) => Ok(StateValue::I4(i32::try_from(self)?)),
|
||||
(_, StateVarType::Int) => Ok(StateValue::Int(i32::try_from(self)?)),
|
||||
|
||||
(_,StateVarType::R8) => Ok(StateValue::R8(f64::try_from(self)?)),
|
||||
(_,StateVarType::Number) => Ok(StateValue::Number(f64::try_from(self)?)),
|
||||
(_,StateVarType::Fixed14_4) => Ok(StateValue::Fixed14_4(f64::try_from(self)?)),
|
||||
(_,StateVarType::R4) => Ok(StateValue::R4(f32::try_from(self)?)),
|
||||
(_, StateVarType::R8) => Ok(StateValue::R8(f64::try_from(self)?)),
|
||||
(_, StateVarType::Number) => Ok(StateValue::Number(f64::try_from(self)?)),
|
||||
(_, StateVarType::Fixed14_4) => Ok(StateValue::Fixed14_4(f64::try_from(self)?)),
|
||||
(_, StateVarType::R4) => Ok(StateValue::R4(f32::try_from(self)?)),
|
||||
|
||||
// --- Pas encore implémenté pour les autres types ---
|
||||
(val, target) => Err(StateValueError::TypeError(format!(
|
||||
|
||||
@@ -24,20 +24,22 @@ 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(
|
||||
DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
|
||||
FixedOffset::east_opt(0).unwrap())
|
||||
),
|
||||
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(),
|
||||
))
|
||||
}
|
||||
StateVarType::Time => StateValue::Time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()),
|
||||
StateVarType::TimeTZ => StateValue::TimeTZ(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
|
||||
FixedOffset::east_opt(0).unwrap())
|
||||
),
|
||||
StateVarType::TimeTZ => StateValue::TimeTZ(DateTime::from_naive_utc_and_offset(
|
||||
DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
|
||||
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 {
|
||||
|
||||
@@ -4,25 +4,25 @@ use thiserror::Error;
|
||||
pub enum StateValueError {
|
||||
#[error("Conversion error: {0}")]
|
||||
ConversionError(String),
|
||||
|
||||
|
||||
#[error("Validation error: {0}")]
|
||||
ValidationError(String),
|
||||
|
||||
|
||||
#[error("Range error: {0}")]
|
||||
RangeError(String),
|
||||
|
||||
|
||||
#[error("Type error: {0}")]
|
||||
TypeError(String),
|
||||
|
||||
|
||||
#[error("Parse error: {0}")]
|
||||
ParseError(String),
|
||||
|
||||
|
||||
#[error("Event condition error: {0}")]
|
||||
EventConditionError(String),
|
||||
|
||||
|
||||
#[error("Arithmetic error: {0}")]
|
||||
ArithmeticError(String),
|
||||
|
||||
|
||||
#[error("Unknown error: {0}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
@@ -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é
|
||||
@@ -30,7 +29,7 @@ impl FromStr for StateVarType {
|
||||
"time.tz" => Ok(StateVarType::TimeTZ),
|
||||
"uuid" => Ok(StateVarType::UUID),
|
||||
"uri" => Ok(StateVarType::URI),
|
||||
_ => Err(format!("Type inconnu: {}", s)),
|
||||
_ => Err(format!("Type inconnu: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +1,8 @@
|
||||
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 {
|
||||
*self
|
||||
*self
|
||||
}
|
||||
|
||||
fn bit_size(&self) -> Option<usize> {
|
||||
@@ -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
|
||||
| StateVarType::Char
|
||||
| StateVarType::BinHex
|
||||
| StateVarType::BinBase64)
|
||||
matches!(
|
||||
self,
|
||||
StateVarType::String
|
||||
| StateVarType::Char
|
||||
| StateVarType::BinHex
|
||||
| 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
|
||||
@@ -12,17 +11,17 @@ impl TryFrom<&StateValue> for i16 {
|
||||
match value {
|
||||
StateValue::I1(v) => Ok(*v as i16),
|
||||
StateValue::I2(v) => Ok(*v),
|
||||
StateValue::I4(v) if *v <= i16::MAX as i32 && *v >= i16::MIN as i32 => Ok(*v as i16),
|
||||
StateValue::Int(v) if *v <= i16::MAX as i32 && *v >= i16::MIN as i32 => Ok(*v as i16),
|
||||
StateValue::I4(v) if *v <= i16::MAX as i32 && *v >= i16::MIN as i32 => Ok(*v as i16),
|
||||
StateValue::Int(v) if *v <= i16::MAX as i32 && *v >= i16::MIN as i32 => Ok(*v as i16),
|
||||
|
||||
StateValue::UI1(v) => Ok(*v as i16),
|
||||
StateValue::UI2(v) if *v <= i16::MAX as u16 => Ok(*v as 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;
|
||||
|
||||
@@ -17,12 +16,12 @@ impl TryFrom<&StateValue> for i32 {
|
||||
// non signés
|
||||
StateValue::UI1(v) => Ok(*v as i32),
|
||||
StateValue::UI2(v) => Ok(*v as i32),
|
||||
StateValue::UI4(v) if *v <= i32::MAX as u32 =>Ok(*v as 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
|
||||
@@ -10,19 +9,19 @@ impl TryFrom<&StateValue> for i8 {
|
||||
|
||||
fn try_from(value: &StateValue) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as i8),
|
||||
StateValue::I2(v) if *v <= i8::MAX as i16 && *v >= i8::MIN as i16 => Ok(*v as i8),
|
||||
StateValue::I4(v) if *v <= i8::MAX as i32 && *v >= i8::MIN as i32=> Ok(*v as i8),
|
||||
StateValue::Int(v) if *v <= i8::MAX as i32 && *v >= i8::MIN as i32=> Ok(*v as i8),
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as i8),
|
||||
StateValue::I2(v) if *v <= i8::MAX as i16 && *v >= i8::MIN as i16 => Ok(*v as i8),
|
||||
StateValue::I4(v) if *v <= i8::MAX as i32 && *v >= i8::MIN as i32 => Ok(*v as i8),
|
||||
StateValue::Int(v) if *v <= i8::MAX as i32 && *v >= i8::MIN as i32 => Ok(*v as i8),
|
||||
|
||||
StateValue::UI1(v) if *v <= i8::MAX as u8 => Ok(*v as i8),
|
||||
StateValue::UI2(v) if *v <= i8::MAX as u16 => Ok(*v as 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
|
||||
@@ -13,16 +11,16 @@ impl TryFrom<&StateValue> for u16 {
|
||||
match value {
|
||||
StateValue::UI1(v) => Ok(*v as Self),
|
||||
StateValue::UI2(v) => Ok(*v),
|
||||
StateValue::UI4(v) if *v <= i16::MAX as u32 => Ok(*v as Self),
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::UI4(v) if *v <= i16::MAX as u32 => Ok(*v as Self),
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::I2(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::I4(v) if *v >= 0 && *v <= u16::MAX as i32 => Ok(*v as Self),
|
||||
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 {
|
||||
@@ -10,16 +9,16 @@ impl TryFrom<&StateValue> for u32 {
|
||||
match value {
|
||||
StateValue::UI1(v) => Ok(*v as Self),
|
||||
StateValue::UI2(v) => Ok(*v as Self),
|
||||
StateValue::UI4(v) => Ok(*v),
|
||||
StateValue::UI4(v) => Ok(*v),
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::I2(v) if *v >= 0 => Ok(*v as Self),
|
||||
StateValue::I4(v) if *v >= 0 => Ok(*v as Self),
|
||||
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
|
||||
@@ -12,16 +11,16 @@ impl TryFrom<&StateValue> for u8 {
|
||||
match value {
|
||||
StateValue::UI1(v) => Ok(*v),
|
||||
StateValue::UI2(v) if *v <= u8::MAX as u16 => Ok(*v as u8),
|
||||
StateValue::UI4(v) if *v <= i8::MAX as u32 => Ok(*v as u8),
|
||||
StateValue::UI4(v) if *v <= i8::MAX as u32 => Ok(*v as u8),
|
||||
StateValue::I1(v) if *v >= 0 => Ok(*v as u8),
|
||||
StateValue::I2(v) if *v >= 0 && *v <= u8::MAX as i16 => Ok(*v as u8),
|
||||
StateValue::I4(v) if *v >= 0 && *v <= u8::MAX as i32 => Ok(*v as 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) => {
|
||||
|
||||
Reference in New Issue
Block a user