netget-tool 0.3.4

Network protocol stubbing toolkit: ~280 protocol servers and clients whose responses come from an LLM, a script, a static rule or a person.
Documentation
use crate::server::gnmi::{
    proto::gnmi as pb,
    semantic::{self, Model},
    value::{self, Path, Update},
};
use serde::Deserialize;
use serde_json::Value;
use tonic::Status;
fn proto() -> String {
    "PROTO".into()
}
fn all() -> String {
    "ALL".into()
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Get {
    #[serde(default)]
    prefix: Path,
    #[serde(default)]
    path: Vec<Path>,
    #[serde(default = "all")]
    data_type: String,
    #[serde(default = "proto")]
    encoding: String,
    #[serde(default)]
    use_models: Vec<Model>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Set {
    #[serde(default)]
    prefix: Path,
    #[serde(default)]
    delete: Vec<Path>,
    #[serde(default)]
    replace: Vec<Update>,
    #[serde(default)]
    update: Vec<Update>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Subscription {
    path: Path,
    #[serde(default)]
    mode: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Subscribe {
    #[serde(default)]
    prefix: Path,
    mode: String,
    subscription: Vec<Subscription>,
    #[serde(default = "proto")]
    encoding: String,
    #[serde(default)]
    updates_only: bool,
    #[serde(default)]
    use_models: Vec<Model>,
}
pub enum Request {
    Capabilities(pb::CapabilityRequest),
    Get(pb::GetRequest),
    Set(pb::SetRequest),
    Subscribe(pb::SubscriptionList),
    Poll,
    Cancel,
    Wait,
    Disconnect,
}
fn decode<T: serde::de::DeserializeOwned>(value: &Value) -> Result<T, Status> {
    serde_json::from_value(value.clone())
        .map_err(|_| Status::invalid_argument("invalid typed gNMI request"))
}
pub fn parse(action: &Value) -> Result<Request, Status> {
    super::check_action(action)?;
    Ok(match action["type"].as_str().unwrap_or("") {
        "disconnect" => Request::Disconnect,
        "wait_for_more" => Request::Wait,
        "gnmi_poll" => Request::Poll,
        "gnmi_cancel" => Request::Cancel,
        "gnmi_capabilities" => Request::Capabilities(pb::CapabilityRequest { extension: vec![] }),
        "gnmi_get" => {
            let request: Get = decode(&action["request"])?;
            let kind = match request.data_type.as_str() {
                "ALL" => 0,
                "CONFIG" => 1,
                "STATE" => 2,
                "OPERATIONAL" => 3,
                _ => return Err(Status::invalid_argument("unknown data_type")),
            };
            let request = value::checked(pb::GetRequest {
                prefix: Some(request.prefix.into_proto()?),
                path: request
                    .path
                    .into_iter()
                    .map(Path::into_proto)
                    .collect::<Result<_, _>>()?,
                r#type: kind,
                encoding: value::encoding(&request.encoding)?,
                use_models: semantic::model_proto(request.use_models)?,
                extension: vec![],
            })?;
            semantic::get_request(&request)?;
            Request::Get(request)
        }
        "gnmi_set" => {
            let request: Set = decode(&action["request"])?;
            let request = value::checked(pb::SetRequest {
                prefix: Some(request.prefix.into_proto()?),
                delete: request
                    .delete
                    .into_iter()
                    .map(Path::into_proto)
                    .collect::<Result<_, _>>()?,
                replace: request
                    .replace
                    .into_iter()
                    .map(Update::into_proto)
                    .collect::<Result<_, _>>()?,
                update: request
                    .update
                    .into_iter()
                    .map(Update::into_proto)
                    .collect::<Result<_, _>>()?,
                union_replace: vec![],
                extension: vec![],
            })?;
            semantic::set_request(&request)?;
            Request::Set(request)
        }
        "gnmi_subscribe" => {
            let request: Subscribe = decode(&action["request"])?;
            let mode = match request.mode.as_str() {
                "STREAM" => 0,
                "ONCE" => 1,
                "POLL" => 2,
                _ => return Err(Status::invalid_argument("unknown subscribe mode")),
            };
            let list = pb::SubscriptionList {
                prefix: Some(request.prefix.into_proto()?),
                subscription: request
                    .subscription
                    .into_iter()
                    .map(|s| {
                        let mode = match s.mode.as_deref().unwrap_or("TARGET_DEFINED") {
                            "TARGET_DEFINED" => 0,
                            "ON_CHANGE" => 1,
                            _ => return Err(Status::unimplemented("SAMPLE is excluded")),
                        };
                        Ok(pb::Subscription {
                            path: Some(s.path.into_proto()?),
                            mode,
                            sample_interval: 0,
                            suppress_redundant: false,
                            heartbeat_interval: 0,
                        })
                    })
                    .collect::<Result<_, Status>>()?,
                qos: None,
                mode,
                allow_aggregation: false,
                use_models: semantic::model_proto(request.use_models)?,
                encoding: value::encoding(&request.encoding)?,
                updates_only: request.updates_only,
            };
            semantic::subscription(&list)?;
            value::checked(pb::SubscribeRequest {
                request: Some(pb::subscribe_request::Request::Subscribe(list.clone())),
                extension: vec![],
            })?;
            Request::Subscribe(list)
        }
        _ => return Err(Status::invalid_argument("unknown gNMI client action")),
    })
}