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
use super::*;
use sys::*;
use std::marker::PhantomData;

/// An `Environment` is a global context, in which to access data.
///
/// Associated with an `Environment` is any information that is global in nature, such as:
///
/// * The `Environment`'s state
/// * The current environment-level diagnostics
/// * The handles of connections currently allocated on the environment
/// * The current stetting of each environment attribute
///
/// See: [Environment Handles in the ODBC Reference][1]
/// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/environment-handles
#[derive(Debug)]
pub struct Environment<V> {
    version: PhantomData<V>,
    /// Invariant: Should always point to a valid ODBC Environment with Version declared as V or
    /// `NoVersion`
    handle: HEnv,
}

impl<V> Environment<V> {
    /// Provides access to the raw ODBC environment handle.
    pub fn as_raw(&self) -> SQLHENV {
        self.handle.as_raw()
    }

    /// Express state transiton
    fn transit<Other>(self) -> Environment<Other> {
        Environment {
            version: PhantomData,
            handle: self.handle,
        }
    }
}

impl<V: Version> Environment<V> {
    /// Used by `Connection`s constructor
    pub(crate) fn as_henv(&self) -> &HEnv {
        &self.handle
    }

    /// Fills buffers with information about the available datasources
    ///
    /// A 32 / 64 Bit Application will only return information about either 32 or 64 Bit
    /// DataSources.
    ///
    /// # Returns
    ///
    /// (server_name_length, description_length)
    ///
    /// See [SQLDataSources][1]
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldatasources-function
    pub fn data_sources(
        &mut self,
        direction: FetchOrientation,
        server_name: &mut [u8],
        description: &mut [u8],
    ) -> ReturnOption<(SQLSMALLINT, SQLSMALLINT)> {
        self.handle.data_sources(
            direction,
            server_name,
            description,
        )
    }

    /// Fills buffers with information about the available datasources
    ///
    /// A 32 / 64 Bit Application will only return information about either 32 or 64 Bit
    /// DataSources.
    ///
    /// # Returns
    ///
    /// (description_length, attributes_length)
    ///
    /// See [SQLDrivers][1]
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
    pub fn drivers(
        &mut self,
        direction: FetchOrientation,
        description: &mut [u8],
        attributes: &mut [u8],
    ) -> ReturnOption<(SQLSMALLINT, SQLSMALLINT)> {
        self.handle.drivers(direction, description, attributes)
    }
}

impl Environment<NoVersion> {
    /// Allocates a new `Environment`
    pub fn new() -> Return<Self> {
        HEnv::allocate().map(|handle| {
            Environment {
                version: PhantomData,
                handle: handle,
            }
        })
    }

    /// Before an application allocates a connection which specification it follows. Currently
    /// these bindings only support ODBC 3.x.
    ///
    /// It is valid to specify ODBC 3.x even then connecting to an ODBC 2.x driver. Applications
    /// must however avoid calling 3.x functionality on 2.x drivers. Since drivers are connected at
    /// runtime, these kind of errors can not be catched by the type system.
    pub fn declare_version<V: Version>(mut self) -> Return<Environment<V>, Environment<NoVersion>> {
        let result = self.handle.declare_version(V::constant());
        match result {
            Success(()) => Success(self.transit()),
            Info(()) => Success(self.transit()),
            Error(()) => Success(self.transit()),
        }
    }

    /// Before an application allocates a connection which specification it follows. Currently
    /// these bindings only support ODBC 3.x.
    ///
    /// It is valid to specify ODBC 3.x even then connecting to an ODBC 2.x driver. Applications
    /// must however avoid calling 3.x functionality on 2.x drivers. Since drivers are connected at
    /// runtime, these kind of errors can not be catched by the type system.
    ///
    /// This method is a shorthand for `declare_version::<Odbc3m8>`.
    pub fn declare_version_3_8(self) -> Return<Environment<Odbc3m8>, Environment<NoVersion>> {
        self.declare_version()
    }

    /// Before an application allocates a connection which specification it follows. Currently
    /// these bindings only support ODBC 3.x.
    ///
    /// It is valid to specify ODBC 3.x even then connecting to an ODBC 2.x driver. Applications
    /// must however avoid calling 3.x functionality on 2.x drivers. Since drivers are connected at
    /// runtime, these kind of errors can not be catched by the type system.
    ///
    /// This method is a shorthand for `declare_version::<Odbc3>`.
    pub fn declare_version_3(self) -> Return<Environment<Odbc3>, Environment<NoVersion>> {
        self.declare_version()
    }
}

impl<V> Diagnostics for Environment<V> {
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        self.handle.diagnostics(rec_number, message_text)
    }
}