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
//!
//! # 6. Versions module
//! Type: Configuration Module
//! This is the required base module of OCPI.
//! This module is the starting point for any OCPI connection.
//! Via this module, clients can learn which versions of OCPI a server supports,
//! and which modules it supports for each of the versions.
//!

use crate::{types, Result};

pub trait VersionsModule {
    /// # 6.1. Version information endpoint
    /// This endpoint lists all the available OCPI versions and the corresponding
    /// URLs to where version specific details such as the
    /// supported endpoints can be found.
    /// Endpoint structure definition:
    ///
    /// No structure defined. This is open for every party to define themselves.
    /// Examples:
    /// https://www.server.com/ocpi/cpo/versions
    /// https://www.server.com/ocpi/emsp/versions
    /// https://ocpi.server.com/versions
    /// The exact URL to the implemented version endpoint should be given (offline)
    /// to parties that want to communicate with your OCPI implementation.
    /// Both, CPOs and eMSPs MUST implement such a version endpoint.
    fn versions_get(&self) -> Result<Vec<types::Version>> {
        Ok(Vec::new())
    }

    /// 6.2. Version details endpoint
    ///
    /// Via the version details, the parties can exchange which modules are implemented
    /// for a specific version of OCPI, which interface role is implemented,
    /// and what the endpoint URL is for this interface.
    /// Parties that are both CPO and eMSP (or a Hub) can implement one
    /// version endpoint that covers both roles.
    /// With the information that is available in the version details,
    /// parties don’t need to implement a separate endpoint per role (CPO or eMSP) anymore.
    /// In practice this means that when a company is both a CPO and an eMSP and it connects
    /// to another party that implements both interfaces, only one OCPI connection is needed.
    ///
    /// __NOTE__
    /// OCPI 2.2 introduces the role field in the version details.
    /// Older versions of OCPI do not support this.
    ///
    /// Endpoint structure definition:
    /// No structure defined. This is open for every party to define themselves.
    /// __Examples__:
    /// `https://www.server.com/ocpi/cpo/2.2`
    /// `https://www.server.com/ocpi/emsp/2.2`
    /// `https://ocpi.server.com/2.2/details`
    ///
    /// This endpoint lists the supported endpoints and their URLs for a specific OCPI version.
    /// To notify the other party that the list of endpoints of your current version has changed,
    /// you can send a PUT request to the corresponding credentials endpoint
    /// (see the credentials chapter).
    ///
    /// Both the CPO and the eMSP MUST implement this endpoint.
    fn versions_get_details(
        &self,
        _version_number: types::VersionNumber,
    ) -> Result<Option<types::VersionDetails>> {
        Ok(None)
    }
}

impl<DB> VersionsModule for crate::Cpo<DB>
where
    DB: crate::Store,
{
    fn versions_get(&self) -> Result<Vec<types::Version>> {
        let url = self
            .base_url
            .join("versions")
            .expect("versions")
            .join("2.2")
            .expect("URL 2.2");

        Ok(vec![types::Version {
            version: types::VersionNumber::V2_2,
            url,
        }])
    }

    fn versions_get_details(
        &self,
        version_number: types::VersionNumber,
    ) -> Result<Option<types::VersionDetails>> {
        match version_number {
            types::VersionNumber::V2_2 => Ok(Some(types::VersionDetails {
                version: types::VersionNumber::V2_2,
                endpoints: vec![types::Endpoint {
                    identifier: types::ModuleId::Credentials,
                    role: types::InterfaceRole::Receiver,
                    url: self
                        .base_url
                        .join("2.2")
                        .expect("Parsing url `2.2`")
                        .join("credentials")
                        .expect("Parsing URL `credentials`"),
                }],
            })),

            version => Ok(Some(types::VersionDetails {
                version,
                endpoints: vec![],
            })),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::types;

    #[test]
    fn test_get_versions() {
        let cpo = crate::cpo();

        let res = cpo.versions_get().expect("getting versions");
        assert_eq!(res.len(), 1);
        assert_eq!(res[0].version, types::VersionNumber::V2_2);
    }

    #[test]
    fn test_get_version_details() {
        let cpo = crate::cpo();

        let res = cpo
            .versions_get_details(types::VersionNumber::V2_2)
            .expect("getting versions");

        assert!(res.is_some());
    }
}