aboutsummaryrefslogtreecommitdiffstats
path: root/third_party/blurmac/src/utils.rs
blob: d2b5c555f7e3c249e2da3989fc7bdf01a29e81a7 (plain) (blame)
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
// Copyright (c) 2017 Akos Kiss.
//
// Licensed under the BSD 3-Clause License
// <LICENSE.md or https://opensource.org/licenses/BSD-3-Clause>.
// This file may not be copied, modified, or distributed except
// according to those terms.

use std::error::Error;
use std::ffi::{CStr, CString};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{thread, time};

use framework::{cb, nil, ns};
use objc::runtime::Object;

pub const NOT_SUPPORTED_ERROR: &'static str = "Error! Not supported by blurmac!";
pub const NO_PERIPHERAL_FOUND: &'static str = "Error! No peripheral found!";
pub const NO_SERVICE_FOUND: &'static str = "Error! No service found!";
pub const NO_CHARACTERISTIC_FOUND: &'static str = "Error! No characteristic found!";

pub mod nsx {
    use super::*;

    pub fn string_to_string(nsstring: *mut Object) -> String {
        if nsstring == nil {
            return String::from("nil");
        }
        unsafe {
            String::from(
                CStr::from_ptr(ns::string_utf8string(nsstring))
                    .to_str()
                    .unwrap(),
            )
        }
    }

    pub fn string_from_str(string: &str) -> *mut Object {
        let cstring = CString::new(string).unwrap();
        ns::string(cstring.as_ptr())
    }
}

pub mod cbx {
    use super::*;

    pub fn uuid_to_canonical_uuid_string(cbuuid: *mut Object) -> String {
        // NOTE: CoreBluetooth tends to return uppercase UUID strings, and only 4 character long if the
        // UUID is short (16 bits). However, WebBluetooth mandates lowercase UUID strings. And Servo
        // seems to compare strings, not the binary representation.
        let uuid = nsx::string_to_string(cb::uuid_uuidstring(cbuuid));
        let long = if uuid.len() == 4 {
            format!("0000{}-0000-1000-8000-00805f9b34fb", uuid)
        } else {
            uuid
        };
        long.to_lowercase()
    }

    pub fn peripheral_debug(peripheral: *mut Object) -> String {
        if peripheral == nil {
            return String::from("nil");
        }
        let name = cb::peripheral_name(peripheral);
        let uuid = ns::uuid_uuidstring(cb::peer_identifier(peripheral));
        if name != nil {
            format!(
                "CBPeripheral({}, {})",
                nsx::string_to_string(name),
                nsx::string_to_string(uuid)
            )
        } else {
            format!("CBPeripheral({})", nsx::string_to_string(uuid))
        }
    }

    pub fn service_debug(service: *mut Object) -> String {
        if service == nil {
            return String::from("nil");
        }
        let uuid = cb::uuid_uuidstring(cb::attribute_uuid(service));
        format!("CBService({})", nsx::string_to_string(uuid))
    }

    pub fn characteristic_debug(characteristic: *mut Object) -> String {
        if characteristic == nil {
            return String::from("nil");
        }
        let uuid = cb::uuid_uuidstring(cb::attribute_uuid(characteristic));
        format!("CBCharacteristic({})", nsx::string_to_string(uuid))
    }
}

pub mod wait {
    use super::*;

    pub type Timestamp = u64;

    static TIMESTAMP: AtomicUsize = AtomicUsize::new(0);

    pub fn get_timestamp() -> Timestamp {
        TIMESTAMP.fetch_add(1, Ordering::SeqCst) as u64
    }

    pub fn now() -> *mut Object {
        ns::number_withunsignedlonglong(get_timestamp())
    }

    pub fn wait_or_timeout<F>(mut f: F) -> Result<(), Box<dyn Error>>
    where
        F: FnMut() -> bool,
    {
        let now = time::Instant::now();

        while !f() {
            thread::sleep(time::Duration::from_secs(1));
            if now.elapsed().as_secs() > 30 {
                return Err(Box::from("timeout"));
            }
        }
        Ok(())
    }
}