macro_traffic_sim 0.1.2

gRPC interface for macroscopic traffic simulation via 4-step demand model
Documentation
use std::sync::{Arc, Mutex};

use tonic::{Code, Request, Response, Status};
use uuid::Uuid;

use macro_traffic_sim::pb;

use super::{MacroSession, SessionsStorage};

pub async fn new_session(
    sessions: Arc<Mutex<SessionsStorage>>,
    _request: Request<pb::NewSessionRequest>,
) -> Result<Response<pb::NewSessionResponse>, Status> {
    let session = MacroSession::new();
    let sid = session.id;

    let mut guard = sessions.lock().map_err(|_| Status::internal("storage poisoned"))?;
    guard.register(session);
    drop(guard);

    let resp = pb::NewSessionResponse {
        code: Code::Ok as u32,
        text: "OK".to_string(),
        session_id: Some(pb::UuiDv4 { value: sid.to_string() }),
    };
    Ok(Response::new(resp))
}

pub async fn info_session(
    sessions: Arc<Mutex<SessionsStorage>>,
    request: Request<pb::SessionId>,
) -> Result<Response<pb::InfoSessionResponse>, Status> {
    let id_msg = request.into_inner().value
        .ok_or_else(|| Status::invalid_argument("missing session_id"))?;
    let sid = Uuid::parse_str(&id_msg.value)
        .map_err(|_| Status::invalid_argument("invalid UUID"))?;

    let mut guard = sessions.lock().map_err(|_| Status::internal("storage poisoned"))?;
    let result = guard.with_session(&sid, |session| {
        pb::InfoSessionResponse {
            code: Code::Ok as u32,
            text: "OK".to_string(),
            session_id: Some(pb::UuiDv4 { value: sid.to_string() }),
            state: session.state.to_string(),
            has_network: !session.network.nodes.is_empty(),
            has_zones: !session.zones.is_empty(),
            has_od_matrix: session.od_matrix.is_some(),
            has_config: session.config.is_some(),
            has_results: !session.results.is_empty(),
            num_nodes: session.network.node_count() as u32,
            num_links: session.network.link_count() as u32,
            num_zones: session.zones.len() as u32,
        }
    });
    drop(guard);

    match result {
        Some(resp) => Ok(Response::new(resp)),
        None => {
            let resp = pb::InfoSessionResponse {
                code: Code::NotFound as u32,
                text: format!("Session not found: {}", sid),
                session_id: None,
                state: String::new(),
                has_network: false,
                has_zones: false,
                has_od_matrix: false,
                has_config: false,
                has_results: false,
                num_nodes: 0,
                num_links: 0,
                num_zones: 0,
            };
            Ok(Response::new(resp))
        }
    }
}

pub async fn delete_session(
    sessions: Arc<Mutex<SessionsStorage>>,
    request: Request<pb::SessionId>,
) -> Result<Response<pb::DeleteSessionResponse>, Status> {
    let id_msg = request.into_inner().value
        .ok_or_else(|| Status::invalid_argument("missing session_id"))?;
    let sid = Uuid::parse_str(&id_msg.value)
        .map_err(|_| Status::invalid_argument("invalid UUID"))?;

    let mut guard = sessions.lock().map_err(|_| Status::internal("storage poisoned"))?;
    let removed = guard.remove(&sid);
    drop(guard);

    if removed {
        Ok(Response::new(pb::DeleteSessionResponse {
            code: Code::Ok as u32,
            text: "OK".to_string(),
        }))
    } else {
        Err(Status::not_found(format!("Session not found: {}", sid)))
    }
}