1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Jenkins Home, describing state of the master

use serde::Deserialize;

use crate::client_internals::{Path, Result};
use crate::job::ShortJob;
use crate::view::ShortView;
use crate::Jenkins;

/// Describe how Jenkins allocates jobs to agents
#[derive(Debug, Deserialize, Clone, Copy)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Mode {
    /// Any job can be started on this node
    Normal,
    /// Only jobs specifically specifying this node can start
    Exclusive,
}

/// Port announced by Jenkins for agent to connect to
#[derive(Debug, Copy, Clone)]
pub enum AgentPort {
    /// TCP connection from agent is disabled
    Disabled,
    /// Port is selected randomly
    Random,
    /// Port is fixed
    Fixed(u32),
}

impl<'de> Deserialize<'de> for AgentPort {
    fn deserialize<D>(deserializer: D) -> std::result::Result<AgentPort, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        deserializer.deserialize_i32(AgentPortI32Visitor)
    }
}
struct AgentPortI32Visitor;
impl<'de> serde::de::Visitor<'de> for AgentPortI32Visitor {
    type Value = AgentPort;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("an integer between -1 and 65536")
    }

    fn visit_i8<E>(self, value: i8) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_i16<E>(self, value: i16) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_i32<E>(self, value: i32) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_i64<E>(self, value: i64) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        match value {
            -1 => Ok(AgentPort::Disabled),
            0 => Ok(AgentPort::Random),
            p if p > 0 && p < 65537 => Ok(AgentPort::Fixed(p as u32)),
            x => Err(serde::de::Error::invalid_value(
                serde::de::Unexpected::Signed(x),
                &"port between -1 and 65536",
            )),
        }
    }

    fn visit_u8<E>(self, value: u8) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_u16<E>(self, value: u16) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_u32<E>(self, value: u32) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        self.visit_i64(i64::from(value))
    }

    fn visit_u64<E>(self, value: u64) -> std::result::Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        match value {
            0 => Ok(AgentPort::Random),
            p if p > 0 && p < 65537 => Ok(AgentPort::Fixed(p as u32)),
            x => Err(serde::de::Error::invalid_value(
                serde::de::Unexpected::Unsigned(x),
                &"port between -1 and 65536",
            )),
        }
    }
}

/// Index of Jenkins, with details about the master, a list of `Job` and a list of `View`
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Home {
    /// Mode of the node for job selections
    pub mode: Mode,
    /// Description of the node
    pub node_description: String,
    /// Name of the node
    pub node_name: String,
    /// Number of executors of the node
    pub num_executors: u32,
    /// Description of the master
    pub description: Option<String>,
    /// List of jobs
    pub jobs: Vec<ShortJob>,
    /// Is Jenkins preparing to restart
    pub quieting_down: bool,
    /// HTTP port to the slave agent
    pub slave_agent_port: AgentPort,
    /// Does this instance use crumbs for CSRF
    pub use_crumbs: bool,
    /// False if this instance is either UNSECURED or NO_AUTHENTICATION
    pub use_security: bool,
    /// List of views
    pub views: Vec<ShortView>,
}

impl Jenkins {
    /// Get Jenkins `Home`
    pub fn get_home(&self) -> Result<Home> {
        Ok(self.get(&Path::Home)?.json()?)
    }
}