<commit_before>use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};

use util::codec::bytes;
use raftserver::{Result, other};
use std::mem;

pub const MIN_KEY: &'static [u8] = &[];
pub const MAX_KEY: &'static [u8] = &[0xFF];

// local is in (0x01, 0x02);
pub const LOCAL_PREFIX: u8 = 0x01;
pub const LOCAL_MIN_KEY: &'static [u8] = &[LOCAL_PREFIX];
pub const LOCAL_MAX_KEY: &'static [u8] = &[LOCAL_PREFIX + 1];
pub const META1_PREFIX: u8 = 0x02;
pub const META2_PREFIX: u8 = 0x03;
pub const META1_PREFIX_KEY: &'static [u8] = &[META1_PREFIX];
pub const META2_PREFIX_KEY: &'static [u8] = &[META2_PREFIX];
pub const META_MIN_KEY: &'static [u8] = &[META1_PREFIX];
pub const META_MAX_KEY: &'static [u8] = &[META2_PREFIX + 1];
pub const META1_MAX_KEY: &'static [u8] = &[META1_PREFIX, MAX_KEY[0]];
pub const META2_MAX_KEY: &'static [u8] = &[META2_PREFIX, MAX_KEY[0]];

pub const DATA_PREFIX: u8 = b'z';
pub const DATA_PREFIX_KEY: &'static [u8] = &[DATA_PREFIX];

// Following keys are all local keys, so the first byte must be 0x01.
pub const STORE_IDENT_KEY: &'static [u8] = &[LOCAL_PREFIX, 0x01];
pub const REGION_ID_PREFIX: u8 = 0x02;
pub const REGION_ID_PREFIX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_ID_PREFIX];
pub const REGION_META_PREFIX: u8 = 0x03;
pub const REGION_META_PREFIX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX];
pub const REGION_META_MIN_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX];
pub const REGION_META_MAX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX + 1];

// Following are the suffix after the local prefix.
// For region id
pub const RAFT_LOG_SUFFIX: u8 = 0x01;
pub const RAFT_HARD_STATE_SUFFIX: u8 = 0x02;
pub const RAFT_APPLIED_INDEX_SUFFIX: u8 = 0x03;
pub const RAFT_LAST_INDEX_SUFFIX: u8 = 0x04;
pub const RAFT_TRUNCATED_STATE_SUFFIX: u8 = 0x05;

// For region meta
pub const REGION_INFO_SUFFIX: u8 = 0x01;

pub fn store_ident_key() -> Vec<u8> {
    STORE_IDENT_KEY.to_vec()
}

fn make_region_id_key(region_id: u64, suffix: u8, extra_cap: usize) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_ID_PREFIX_KEY.len() + mem::size_of::<u64>() +
                                     mem::size_of::<u8>() +
                                     extra_cap);
    key.extend_from_slice(REGION_ID_PREFIX_KEY);
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(region_id).unwrap();
    key.push(suffix);
    key
}

pub fn region_id_prefix(region_id: u64) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_ID_PREFIX_KEY.len() + mem::size_of::<u64>());
    key.extend_from_slice(REGION_ID_PREFIX_KEY);
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(region_id).unwrap();
    key
}

pub fn raft_log_key(region_id: u64, log_index: u64) -> Vec<u8> {
    let mut key = make_region_id_key(region_id, RAFT_LOG_SUFFIX, mem::size_of::<u64>());
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(log_index).unwrap();
    key
}

pub fn raft_log_prefix(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_LOG_SUFFIX, 0)
}

pub fn raft_hard_state_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_HARD_STATE_SUFFIX, 0)
}

pub fn raft_applied_index_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_APPLIED_INDEX_SUFFIX, 0)
}

pub fn raft_last_index_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_LAST_INDEX_SUFFIX, 0)
}

pub fn raft_truncated_state_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_TRUNCATED_STATE_SUFFIX, 0)
}

fn make_region_meta_key(region_key: &[u8], suffix: u8) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_META_PREFIX_KEY.len() + 1 +
                                     bytes::max_encoded_bytes_size(region_key.len()));
    key.extend_from_slice(REGION_META_PREFIX_KEY);
    key.extend(bytes::encode_bytes(region_key));
    key.push(suffix);
    key
}

// Decode encoded region meta key, return the region key and meta suffix type.
pub fn decode_region_meta_key(key: &[u8]) -> Result<(Vec<u8>, u8)> {
    if !key.starts_with(REGION_META_PREFIX_KEY) {
        return Err(other(format!("invalid region meta prefix for key {:?}", key)));
    }

    let (region_key, cnt) = try!(bytes::decode_bytes(&key[REGION_META_PREFIX_KEY.len()..]));
    // The last character must be the suffix type, 1 byte.
    if REGION_META_PREFIX_KEY.len() + cnt + 1 != key.len() {
        return Err(other(format!("invalid region meta suffix for key {:?}", key)));
    }

    Ok((region_key, key[key.len() - 1]))
}

pub fn region_meta_prefix(region_key: &[u8]) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_META_PREFIX_KEY.len() +
                                     bytes::max_encoded_bytes_size(region_key.len()));
    key.extend_from_slice(REGION_META_PREFIX_KEY);
    key.extend(bytes::encode_bytes(region_key));
    key
}

pub fn region_info_key(region_key: &[u8]) -> Vec<u8> {
    make_region_meta_key(region_key, REGION_INFO_SUFFIX)
}

// Returns a region route meta (meta1, meta2) indexing key for the
// given key.
// For data key, it returns a meta2 key, e.g, "zabc" -> \0x03"zabc"
// For meta2 key, it returns a meta1 key, e.g, \0x03\"zabc" -> \0x02"zabc"
// For meta1 key, it returns a MIN_KEY, e.g, \x02\"zabc" -> ""
pub fn region_route_meta_key(key: &[u8]) -> Vec<u8> {
    if key.len() == 0 {
        return MIN_KEY.to_vec();
    }

    match key[0] {
        META1_PREFIX => MIN_KEY.to_vec(),
        META2_PREFIX => vec![META1_PREFIX_KEY, &key[1..]].concat(),
        _ => vec![META2_PREFIX_KEY, key].concat(),
    }
}

pub fn validate_region_route_meta_key(key: &[u8]) -> Result<()> {
    if key == MIN_KEY {
        return Ok(());
    }

    // TODO: Maybe no necessary to check this, remove later?
    if key.len() < META1_PREFIX_KEY.len() {
        return Err(other(format!("{:?} is too short", key)));
    }

    let prefix = key[0];
    if prefix != META2_PREFIX && prefix != META1_PREFIX {
        return Err(other(format!("{:?} is not a meta key", key)));
    }

    // TODO: check data prefix later?
    if MAX_KEY < &key[META1_PREFIX_KEY.len()..] {
        return Err(other(format!("{:?} is > {:?}", key, MAX_KEY)));
    }

    Ok(())
}

pub fn validate_data_key(key: &[u8]) -> Result<()> {
    if !key.starts_with(DATA_PREFIX_KEY) {
        return Err(other(format!("invalid data key {:?}, must start with {}",
                                 key,
                                 DATA_PREFIX)));
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cmp::Ordering;

    #[test]
    fn test_region_id_key() {
        let region_ids = vec![0, 1, 1024, !0];
        for region_id in region_ids {
            let prefix = region_id_prefix(region_id);

            assert!(raft_log_prefix(region_id).starts_with(&prefix));
            assert!(raft_log_key(region_id, 1).starts_with(&prefix));
            assert!(raft_hard_state_key(region_id).starts_with(&prefix));
            assert!(raft_applied_index_key(region_id).starts_with(&prefix));
            assert!(raft_last_index_key(region_id).starts_with(&prefix));
            assert!(raft_truncated_state_key(region_id).starts_with(&prefix));
        }

        // test sort.
        let tbls = vec![(1, 0, Ordering::Greater), (1, 1, Ordering::Equal), (1, 2, Ordering::Less)];
        for (lid, rid, order) in tbls {
            let lhs = region_id_prefix(lid);
            let rhs = region_id_prefix(rid);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    #[test]
    fn test_raft_log_sort() {
        let tbls = vec![(1, 1, 1, 2, Ordering::Less),
                        (2, 1, 1, 2, Ordering::Greater),
                        (1, 1, 1, 1, Ordering::Equal)];

        for (lid, l_log_id, rid, r_log_id, order) in tbls {
            let lhs = raft_log_key(lid, l_log_id);
            let rhs = raft_log_key(rid, r_log_id);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    #[test]
    fn test_region_meta_key() {
        let keys: Vec<&[u8]> = vec![b"123", b"abc", b"hello_world"];
        for key in keys {
            let prefix = region_meta_prefix(key);
            let info_key = region_info_key(key);
            assert!(info_key.starts_with(&prefix));

            assert_eq!(decode_region_meta_key(&info_key).unwrap(),
                       (key.to_vec(), REGION_INFO_SUFFIX));
        }

        // test sort.
        let tbls: Vec<(&[u8], &[u8], Ordering)> = vec![
        (b"1", b"2", Ordering::Less),
        (b"1", b"1", Ordering::Equal),
        (b"2", b"1", Ordering::Greater),
        (b"2", b"123", Ordering::Greater),
        ];

        for (lkey, rkey, order) in tbls {
            let lhs = region_info_key(lkey);
            let rhs = region_info_key(rkey);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    fn route_key(prefix: u8, key: &[u8]) -> Vec<u8> {
        let mut v = vec![];
        v.push(prefix);
        v.extend_from_slice(key);
        v
    }

    fn meta1_key(key: &[u8]) -> Vec<u8> {
        route_key(META1_PREFIX, key)
    }

    fn meta2_key(key: &[u8]) -> Vec<u8> {
        route_key(META2_PREFIX, key)
    }

    fn data_key(key: &[u8]) -> Vec<u8> {
        route_key(DATA_PREFIX, key)
    }

    #[test]
    fn test_region_route_meta_key() {
        let dkey = data_key(b"abc");
        let tbls = vec![
            (vec![], vec![]),
            (meta1_key(&dkey), vec![]),
            (meta2_key(&dkey), meta1_key(&dkey)),
            (dkey.clone(), meta2_key(&dkey)),
        ];

        for (lkey, rkey) in tbls {
            assert_eq!(region_route_meta_key(&lkey), rkey);
        }

        let tbls = vec![
            (vec![], true),
            (vec![0xFF], false),
            (vec![DATA_PREFIX, 1], false),
            (vec![META1_PREFIX - 1, 1], false),
            (vec![META2_PREFIX + 1, 1], false),
            (vec![META1_PREFIX, 1], true),
            (vec![META2_PREFIX, 1], true),
            (vec![META1_PREFIX, 0xFF, 1], false),
            (vec![META2_PREFIX, 0xFF, 1], false),
        ];

        for (key, ok) in tbls {
            match validate_region_route_meta_key(&key) {
                Ok(_) => assert!(ok),
                Err(_) => assert!(!ok),
            }
        }
    }

    #[test]
    fn test_data_key() {
        validate_data_key(&data_key(b"abc")).unwrap();
        validate_data_key(b"abc").unwrap_err();
    }
}
<commit_msg>raftserver: Address comment.<commit_after>use std::vec::Vec;

use byteorder::{BigEndian, WriteBytesExt};

use util::codec::bytes;
use raftserver::{Result, other};
use std::mem;

pub const MIN_KEY: &'static [u8] = &[];
pub const MAX_KEY: &'static [u8] = &[0xFF];

// local is in (0x01, 0x02);
pub const LOCAL_PREFIX: u8 = 0x01;
pub const LOCAL_MIN_KEY: &'static [u8] = &[LOCAL_PREFIX];
pub const LOCAL_MAX_KEY: &'static [u8] = &[LOCAL_PREFIX + 1];
pub const META1_PREFIX: u8 = 0x02;
pub const META2_PREFIX: u8 = 0x03;
pub const META1_PREFIX_KEY: &'static [u8] = &[META1_PREFIX];
pub const META2_PREFIX_KEY: &'static [u8] = &[META2_PREFIX];
pub const META_MIN_KEY: &'static [u8] = &[META1_PREFIX];
pub const META_MAX_KEY: &'static [u8] = &[META2_PREFIX + 1];
pub const META1_MAX_KEY: &'static [u8] = &[META1_PREFIX, MAX_KEY[0]];
pub const META2_MAX_KEY: &'static [u8] = &[META2_PREFIX, MAX_KEY[0]];

pub const DATA_PREFIX: u8 = b'z';
pub const DATA_PREFIX_KEY: &'static [u8] = &[DATA_PREFIX];

// Following keys are all local keys, so the first byte must be 0x01.
pub const STORE_IDENT_KEY: &'static [u8] = &[LOCAL_PREFIX, 0x01];
pub const REGION_ID_PREFIX: u8 = 0x02;
pub const REGION_ID_PREFIX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_ID_PREFIX];
pub const REGION_META_PREFIX: u8 = 0x03;
pub const REGION_META_PREFIX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX];
pub const REGION_META_MIN_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX];
pub const REGION_META_MAX_KEY: &'static [u8] = &[LOCAL_PREFIX, REGION_META_PREFIX + 1];

// Following are the suffix after the local prefix.
// For region id
pub const RAFT_LOG_SUFFIX: u8 = 0x01;
pub const RAFT_HARD_STATE_SUFFIX: u8 = 0x02;
pub const RAFT_APPLIED_INDEX_SUFFIX: u8 = 0x03;
pub const RAFT_LAST_INDEX_SUFFIX: u8 = 0x04;
pub const RAFT_TRUNCATED_STATE_SUFFIX: u8 = 0x05;

// For region meta
pub const REGION_INFO_SUFFIX: u8 = 0x01;

pub fn store_ident_key() -> Vec<u8> {
    STORE_IDENT_KEY.to_vec()
}

fn make_region_id_key(region_id: u64, suffix: u8, extra_cap: usize) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_ID_PREFIX_KEY.len() + mem::size_of::<u64>() +
                                     mem::size_of::<u8>() +
                                     extra_cap);
    key.extend_from_slice(REGION_ID_PREFIX_KEY);
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(region_id).unwrap();
    key.push(suffix);
    key
}

pub fn region_id_prefix(region_id: u64) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_ID_PREFIX_KEY.len() + mem::size_of::<u64>());
    key.extend_from_slice(REGION_ID_PREFIX_KEY);
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(region_id).unwrap();
    key
}

pub fn raft_log_key(region_id: u64, log_index: u64) -> Vec<u8> {
    let mut key = make_region_id_key(region_id, RAFT_LOG_SUFFIX, mem::size_of::<u64>());
    // no need check error here, can't panic;
    key.write_u64::<BigEndian>(log_index).unwrap();
    key
}

pub fn raft_log_prefix(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_LOG_SUFFIX, 0)
}

pub fn raft_hard_state_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_HARD_STATE_SUFFIX, 0)
}

pub fn raft_applied_index_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_APPLIED_INDEX_SUFFIX, 0)
}

pub fn raft_last_index_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_LAST_INDEX_SUFFIX, 0)
}

pub fn raft_truncated_state_key(region_id: u64) -> Vec<u8> {
    make_region_id_key(region_id, RAFT_TRUNCATED_STATE_SUFFIX, 0)
}

fn make_region_meta_key(region_key: &[u8], suffix: u8) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_META_PREFIX_KEY.len() + 1 +
                                     bytes::max_encoded_bytes_size(region_key.len()));
    key.extend_from_slice(REGION_META_PREFIX_KEY);
    key.extend(bytes::encode_bytes(region_key));
    key.push(suffix);
    key
}

// Decode encoded region meta key, return the region key and meta suffix type.
pub fn decode_region_meta_key(key: &[u8]) -> Result<(Vec<u8>, u8)> {
    if !key.starts_with(REGION_META_PREFIX_KEY) {
        return Err(other(format!("invalid region meta prefix for key {:?}", key)));
    }

    let (region_key, cnt) = try!(bytes::decode_bytes(&key[REGION_META_PREFIX_KEY.len()..]));
    // The last character must be the suffix type, 1 byte.
    if REGION_META_PREFIX_KEY.len() + cnt + 1 != key.len() {
        return Err(other(format!("invalid region meta suffix for key {:?}", key)));
    }

    Ok((region_key, key[key.len() - 1]))
}

pub fn region_meta_prefix(region_key: &[u8]) -> Vec<u8> {
    let mut key = Vec::with_capacity(REGION_META_PREFIX_KEY.len() +
                                     bytes::max_encoded_bytes_size(region_key.len()));
    key.extend_from_slice(REGION_META_PREFIX_KEY);
    key.extend(bytes::encode_bytes(region_key));
    key
}

pub fn region_info_key(region_key: &[u8]) -> Vec<u8> {
    make_region_meta_key(region_key, REGION_INFO_SUFFIX)
}

// Returns a region route meta (meta1, meta2) indexing key for the
// given key.
// For data key, it returns a meta2 key, e.g, "zabc" -> \0x03"zabc"
// For meta2 key, it returns a meta1 key, e.g, \0x03\"zabc" -> \0x02"zabc"
// For meta1 key, it returns a MIN_KEY, e.g, \x02\"zabc" -> ""
pub fn region_route_meta_key(key: &[u8]) -> Vec<u8> {
    if key.len() == 0 {
        return MIN_KEY.to_vec();
    }

    match key[0] {
        META1_PREFIX => MIN_KEY.to_vec(),
        META2_PREFIX => vec![META1_PREFIX_KEY, &key[1..]].concat(),
        _ => vec![META2_PREFIX_KEY, key].concat(),
    }
}

pub fn validate_region_route_meta_key(key: &[u8]) -> Result<()> {
    if key == MIN_KEY {
        return Ok(());
    }

    // TODO: Maybe no necessary to check this, remove later?
    if key.len() < META1_PREFIX_KEY.len() {
        return Err(other(format!("{:?} is too short", key)));
    }

    let prefix = key[0];
    if prefix != META2_PREFIX && prefix != META1_PREFIX {
        return Err(other(format!("{:?} is not a meta key", key)));
    }

    // TODO: check data prefix later?
    if MAX_KEY < &key[META1_PREFIX_KEY.len()..] {
        return Err(other(format!("{:?} is > {:?}", key, MAX_KEY)));
    }

    Ok(())
}

pub fn validate_data_key(key: &[u8]) -> Result<()> {
    if !key.starts_with(DATA_PREFIX_KEY) {
        return Err(other(format!("invalid data key {:?}, must start with {}",
                                 key,
                                 DATA_PREFIX)));
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cmp::Ordering;

    #[test]
    fn test_region_id_key() {
        let region_ids = vec![0, 1, 1024, ::std::u64::MAX];
        for region_id in region_ids {
            let prefix = region_id_prefix(region_id);

            assert!(raft_log_prefix(region_id).starts_with(&prefix));
            assert!(raft_log_key(region_id, 1).starts_with(&prefix));
            assert!(raft_hard_state_key(region_id).starts_with(&prefix));
            assert!(raft_applied_index_key(region_id).starts_with(&prefix));
            assert!(raft_last_index_key(region_id).starts_with(&prefix));
            assert!(raft_truncated_state_key(region_id).starts_with(&prefix));
        }

        // test sort.
        let tbls = vec![(1, 0, Ordering::Greater), (1, 1, Ordering::Equal), (1, 2, Ordering::Less)];
        for (lid, rid, order) in tbls {
            let lhs = region_id_prefix(lid);
            let rhs = region_id_prefix(rid);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    #[test]
    fn test_raft_log_sort() {
        let tbls = vec![(1, 1, 1, 2, Ordering::Less),
                        (2, 1, 1, 2, Ordering::Greater),
                        (1, 1, 1, 1, Ordering::Equal)];

        for (lid, l_log_id, rid, r_log_id, order) in tbls {
            let lhs = raft_log_key(lid, l_log_id);
            let rhs = raft_log_key(rid, r_log_id);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    #[test]
    fn test_region_meta_key() {
        let keys: Vec<&[u8]> = vec![b"123", b"abc", b"hello_world"];
        for key in keys {
            let prefix = region_meta_prefix(key);
            let info_key = region_info_key(key);
            assert!(info_key.starts_with(&prefix));

            assert_eq!(decode_region_meta_key(&info_key).unwrap(),
                       (key.to_vec(), REGION_INFO_SUFFIX));
        }

        // test sort.
        let tbls: Vec<(&[u8], &[u8], Ordering)> = vec![
        (b"1", b"2", Ordering::Less),
        (b"1", b"1", Ordering::Equal),
        (b"2", b"1", Ordering::Greater),
        (b"2", b"123", Ordering::Greater),
        ];

        for (lkey, rkey, order) in tbls {
            let lhs = region_info_key(lkey);
            let rhs = region_info_key(rkey);
            assert_eq!(lhs.partial_cmp(&rhs), Some(order));
        }
    }

    fn route_key(prefix: u8, key: &[u8]) -> Vec<u8> {
        let mut v = vec![];
        v.push(prefix);
        v.extend_from_slice(key);
        v
    }

    fn meta1_key(key: &[u8]) -> Vec<u8> {
        route_key(META1_PREFIX, key)
    }

    fn meta2_key(key: &[u8]) -> Vec<u8> {
        route_key(META2_PREFIX, key)
    }

    fn data_key(key: &[u8]) -> Vec<u8> {
        route_key(DATA_PREFIX, key)
    }

    #[test]
    fn test_region_route_meta_key() {
        let dkey = data_key(b"abc");
        let tbls = vec![
            (vec![], vec![]),
            (meta1_key(&dkey), vec![]),
            (meta2_key(&dkey), meta1_key(&dkey)),
            (dkey.clone(), meta2_key(&dkey)),
        ];

        for (lkey, rkey) in tbls {
            assert_eq!(region_route_meta_key(&lkey), rkey);
        }

        let tbls = vec![
            (vec![], true),
            (vec![0xFF], false),
            (vec![DATA_PREFIX, 1], false),
            (vec![META1_PREFIX - 1, 1], false),
            (vec![META2_PREFIX + 1, 1], false),
            (vec![META1_PREFIX, 1], true),
            (vec![META2_PREFIX, 1], true),
            (vec![META1_PREFIX, 0xFF, 1], false),
            (vec![META2_PREFIX, 0xFF, 1], false),
        ];

        for (key, ok) in tbls {
            match validate_region_route_meta_key(&key) {
                Ok(_) => assert!(ok),
                Err(_) => assert!(!ok),
            }
        }
    }

    #[test]
    fn test_data_key() {
        validate_data_key(&data_key(b"abc")).unwrap();
        validate_data_key(b"abc").unwrap_err();
    }
}
<|endoftext|>


<commit_before>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::{self, SourceRootConfig},
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) proc_macro_changed: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            proc_macro_changed: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        // A file was added or deleted
        let mut has_structure_changes = false;

        let (change, changed_files) = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in &changed_files {
                if let Some(path) = vfs.file_path(file.file_id).as_path() {
                    let path = path.to_path_buf();
                    if reload::should_refresh_for_change(&path, file.change_kind) {
                        self.fetch_workspaces_queue.request_op();
                    }
                    fs_changes.push((path, file.change_kind));
                    if file.is_created_or_deleted() {
                        has_structure_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    String::from_utf8(bytes).ok().and_then(|text| {
                        let (text, line_endings) = LineEndings::normalize(text);
                        line_endings_map.insert(file.file_id, line_endings);
                        Some(Arc::new(text))
                    })
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_structure_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            (change, changed_files)
        };

        self.analysis_host.apply_change(change);

        let raw_database = &self.analysis_host.raw_database();
        self.proc_macro_changed =
            changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
                let crates = raw_database.relevant_crates(file.file_id);
                let crate_graph = raw_database.crate_graph();

                crates.iter().any(|&krate| !crate_graph[krate].is_proc_macro)
            });
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }

    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self
            .req_queue
            .outgoing
            .complete(response.id.clone())
            .expect("received response for unknown request");
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }

    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }

    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation();
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<commit_msg>fix: Fix proc-macro change check being inverted<commit_after>//! The context or environment in which the language server functions. In our
//! server implementation this is know as the `WorldState`.
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.

use std::{sync::Arc, time::Instant};

use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::FxHashMap;
use vfs::AnchoredPathBuf;

use crate::{
    config::Config,
    diagnostics::{CheckFixes, DiagnosticCollection},
    from_proto,
    line_index::{LineEndings, LineIndex},
    lsp_ext,
    main_loop::Task,
    mem_docs::MemDocs,
    op_queue::OpQueue,
    reload::{self, SourceRootConfig},
    thread_pool::TaskPool,
    to_proto::url_from_abs_path,
    Result,
};

// Enforces drop order
pub(crate) struct Handle<H, C> {
    pub(crate) handle: H,
    pub(crate) receiver: C,
}

pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;

/// `GlobalState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
pub(crate) struct GlobalState {
    sender: Sender<lsp_server::Message>,
    req_queue: ReqQueue,
    pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
    pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
    pub(crate) config: Arc<Config>,
    pub(crate) analysis_host: AnalysisHost,
    pub(crate) diagnostics: DiagnosticCollection,
    pub(crate) mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    pub(crate) shutdown_requested: bool,
    pub(crate) proc_macro_changed: bool,
    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
    pub(crate) source_root_config: SourceRootConfig,
    pub(crate) proc_macro_client: Option<ProcMacroServer>,

    pub(crate) flycheck: Vec<FlycheckHandle>,
    pub(crate) flycheck_sender: Sender<flycheck::Message>,
    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,

    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) vfs_config_version: u32,
    pub(crate) vfs_progress_config_version: u32,
    pub(crate) vfs_progress_n_total: usize,
    pub(crate) vfs_progress_n_done: usize,

    /// `workspaces` field stores the data we actually use, while the `OpQueue`
    /// stores the result of the last fetch.
    ///
    /// If the fetch (partially) fails, we do not update the current value.
    ///
    /// The handling of build data is subtle. We fetch workspace in two phases:
    ///
    /// *First*, we run `cargo metadata`, which gives us fast results for
    /// initial analysis.
    ///
    /// *Second*, we run `cargo check` which runs build scripts and compiles
    /// proc macros.
    ///
    /// We need both for the precise analysis, but we want rust-analyzer to be
    /// at least partially available just after the first phase. That's because
    /// first phase is much faster, and is much less likely to fail.
    ///
    /// This creates a complication -- by the time the second phase completes,
    /// the results of the fist phase could be invalid. That is, while we run
    /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
    /// `cargo metadata` completes before `cargo check`.
    ///
    /// An additional complication is that we want to avoid needless work. When
    /// the user just adds comments or whitespace to Cargo.toml, we do not want
    /// to invalidate any salsa caches.
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
    pub(crate) fetch_workspaces_queue: OpQueue<Vec<anyhow::Result<ProjectWorkspace>>>,
    pub(crate) fetch_build_data_queue:
        OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,

    pub(crate) prime_caches_queue: OpQueue<()>,
}

/// An immutable snapshot of the world's state at a point in time.
pub(crate) struct GlobalStateSnapshot {
    pub(crate) config: Arc<Config>,
    pub(crate) analysis: Analysis,
    pub(crate) check_fixes: CheckFixes,
    mem_docs: MemDocs,
    pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
    pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
}

impl std::panic::UnwindSafe for GlobalStateSnapshot {}

impl GlobalState {
    pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
        let loader = {
            let (sender, receiver) = unbounded::<vfs::loader::Message>();
            let handle: vfs_notify::NotifyHandle =
                vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
            let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
            Handle { handle, receiver }
        };

        let task_pool = {
            let (sender, receiver) = unbounded();
            let handle = TaskPool::new(sender);
            Handle { handle, receiver }
        };

        let analysis_host = AnalysisHost::new(config.lru_capacity());
        let (flycheck_sender, flycheck_receiver) = unbounded();
        let mut this = GlobalState {
            sender,
            req_queue: ReqQueue::default(),
            task_pool,
            loader,
            config: Arc::new(config.clone()),
            analysis_host,
            diagnostics: Default::default(),
            mem_docs: MemDocs::default(),
            semantic_tokens_cache: Arc::new(Default::default()),
            shutdown_requested: false,
            proc_macro_changed: false,
            last_reported_status: None,
            source_root_config: SourceRootConfig::default(),
            proc_macro_client: None,

            flycheck: Vec::new(),
            flycheck_sender,
            flycheck_receiver,

            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
            vfs_config_version: 0,
            vfs_progress_config_version: 0,
            vfs_progress_n_total: 0,
            vfs_progress_n_done: 0,

            workspaces: Arc::new(Vec::new()),
            fetch_workspaces_queue: OpQueue::default(),
            prime_caches_queue: OpQueue::default(),

            fetch_build_data_queue: OpQueue::default(),
        };
        // Apply any required database inputs from the config.
        this.update_configuration(config);
        this
    }

    pub(crate) fn process_changes(&mut self) -> bool {
        let _p = profile::span("GlobalState::process_changes");
        let mut fs_changes = Vec::new();
        // A file was added or deleted
        let mut has_structure_changes = false;

        let (change, changed_files) = {
            let mut change = Change::new();
            let (vfs, line_endings_map) = &mut *self.vfs.write();
            let changed_files = vfs.take_changes();
            if changed_files.is_empty() {
                return false;
            }

            for file in &changed_files {
                if let Some(path) = vfs.file_path(file.file_id).as_path() {
                    let path = path.to_path_buf();
                    if reload::should_refresh_for_change(&path, file.change_kind) {
                        self.fetch_workspaces_queue.request_op();
                    }
                    fs_changes.push((path, file.change_kind));
                    if file.is_created_or_deleted() {
                        has_structure_changes = true;
                    }
                }

                let text = if file.exists() {
                    let bytes = vfs.file_contents(file.file_id).to_vec();
                    String::from_utf8(bytes).ok().and_then(|text| {
                        let (text, line_endings) = LineEndings::normalize(text);
                        line_endings_map.insert(file.file_id, line_endings);
                        Some(Arc::new(text))
                    })
                } else {
                    None
                };
                change.change_file(file.file_id, text);
            }
            if has_structure_changes {
                let roots = self.source_root_config.partition(vfs);
                change.set_roots(roots);
            }
            (change, changed_files)
        };

        self.analysis_host.apply_change(change);

        let raw_database = &self.analysis_host.raw_database();
        self.proc_macro_changed =
            changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
                let crates = raw_database.relevant_crates(file.file_id);
                let crate_graph = raw_database.crate_graph();

                crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
            });
        true
    }

    pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
        GlobalStateSnapshot {
            config: Arc::clone(&self.config),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            check_fixes: Arc::clone(&self.diagnostics.check_fixes),
            mem_docs: self.mem_docs.clone(),
            semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
        }
    }

    pub(crate) fn send_request<R: lsp_types::request::Request>(
        &mut self,
        params: R::Params,
        handler: ReqHandler,
    ) {
        let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
        self.send(request.into());
    }

    pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
        let handler = self
            .req_queue
            .outgoing
            .complete(response.id.clone())
            .expect("received response for unknown request");
        handler(self, response)
    }

    pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
        &mut self,
        params: N::Params,
    ) {
        let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
        self.send(not.into());
    }

    pub(crate) fn register_request(
        &mut self,
        request: &lsp_server::Request,
        request_received: Instant,
    ) {
        self.req_queue
            .incoming
            .register(request.id.clone(), (request.method.clone(), request_received));
    }

    pub(crate) fn respond(&mut self, response: lsp_server::Response) {
        if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
            if let Some(err) = &response.error {
                if err.message.starts_with("server panicked") {
                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
                }
            }

            let duration = start.elapsed();
            tracing::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
            self.send(response.into());
        }
    }

    pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
        if let Some(response) = self.req_queue.incoming.cancel(request_id) {
            self.send(response.into());
        }
    }

    fn send(&mut self, message: lsp_server::Message) {
        self.sender.send(message).unwrap()
    }
}

impl Drop for GlobalState {
    fn drop(&mut self) {
        self.analysis_host.request_cancellation();
    }
}

impl GlobalStateSnapshot {
    pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
        url_to_file_id(&self.vfs.read().0, url)
    }

    pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
        file_id_to_url(&self.vfs.read().0, id)
    }

    pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
        let endings = self.vfs.read().1[&file_id];
        let index = self.analysis.file_line_index(file_id)?;
        let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
        Ok(res)
    }

    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
        let path = from_proto::vfs_path(url).ok()?;
        Some(self.mem_docs.get(&path)?.version)
    }

    pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
        let mut base = self.vfs.read().0.file_path(path.anchor);
        base.pop();
        let path = base.join(&path.path).unwrap();
        let path = path.as_path().unwrap();
        url_from_abs_path(path)
    }

    pub(crate) fn cargo_target_for_crate_root(
        &self,
        crate_id: CrateId,
    ) -> Option<(&CargoWorkspace, Target)> {
        let file_id = self.analysis.crate_root(crate_id).ok()?;
        let path = self.vfs.read().0.file_path(file_id);
        let path = path.as_path()?;
        self.workspaces.iter().find_map(|ws| match ws {
            ProjectWorkspace::Cargo { cargo, .. } => {
                cargo.target_by_root(path).map(|it| (cargo, it))
            }
            ProjectWorkspace::Json { .. } => None,
            ProjectWorkspace::DetachedFiles { .. } => None,
        })
    }
}

pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
    let path = vfs.file_path(id);
    let path = path.as_path().unwrap();
    url_from_abs_path(path)
}

pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
    let path = from_proto::vfs_path(url)?;
    let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
    Ok(res)
}
<|endoftext|>


<commit_before>use getopts::{optflag,usage,OptGroup};
use std::io::{println,print};

pub fn opts() -> [OptGroup, .. 2]{
    return [
        optflag("v", "version", "display the version"),
        optflag("h", "help", "print this help menu")
    ]
}

#[allow(dead_code)]
pub fn print_usage(_program: &str, opts: &[OptGroup]) {
    let space = "    ";
    println("Usage: muxed <command> [options]");
    println("");
    println("Commands:");
    println(format!("{}new [name]          create a new project file", space).as_slice());
    print(  format!("{}open [name]         open an existing project file", space).as_slice());
    println(usage("", opts).as_slice());
}

#[test]
fn opts_returns_two_options() {
  assert_eq!(opts().len(), 2);
}
<commit_msg>Add periods.<commit_after>use getopts::{optflag,usage,OptGroup};
use std::io::{println,print};

pub fn opts() -> [OptGroup, .. 2]{
    return [
        optflag("v", "version", "display the version."),
        optflag("h", "help", "print this help menu.")
    ]
}

#[allow(dead_code)]
pub fn print_usage(opts: &[OptGroup]) {
    let space = "    ";
    println("Usage: muxed <command> [options]");
    println("");
    println("Commands:");
    println(format!("{}new [name]          create a new project file.", space).as_slice());
    print(  format!("{}open [name]         open an existing project file.", space).as_slice());
    println(usage("", opts).as_slice());
}

#[test]
fn opts_returns_two_options() {
  assert_eq!(opts().len(), 2);
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate log;
extern crate serde;
extern crate serde_json;

use super::config;
use hyper::{Client, Error};
use hyper::client::response::Response;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use serde::Serialize;
use std::io::Read;

pub enum HttpStatus {
    Ok,
    Other,
}

#[derive(Serialize)]
struct Empty();

pub fn simple_post(path: String) -> Result<Response, Error> {
    post(path, &Empty {})
}

pub fn post<T>(path: String, payload: &T) -> Result<Response, Error>
where
    T: Serialize,
{
    let mut url = config::base_url();
    url.push_str(&path);
    debug!("Request URL: {}", url);

    let body = serde_json::to_string(&payload).unwrap();
    debug!("Request body: {}", body);

    let mut headers = Headers::new();
    headers.set(ContentType(
        Mime(TopLevel::Application, SubLevel::Json, vec![]),
    ));

    let client = Client::new();
    let mut builder = client.post(&url);
    builder = builder.headers(headers);
    builder = builder.body(&body[..]);
    builder.send()
}

pub fn extract_response(mut response: Response) -> (HttpStatus, String) {
    debug!("Response status: {}", response.status);
    let status = match response.status {
        StatusCode::Ok => HttpStatus::Ok,
        _ => HttpStatus::Other,
    };

    let mut body = String::new();
    response.read_to_string(&mut body).unwrap();;
    debug!("Response body: {}", body);

    (status, body)
}<commit_msg>use format marco for string concat<commit_after>extern crate hyper;
extern crate log;
extern crate serde;
extern crate serde_json;

use super::config;
use hyper::{Client, Error};
use hyper::client::response::Response;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use serde::Serialize;
use std::io::Read;

pub enum HttpStatus {
    Ok,
    Other,
}

#[derive(Serialize)]
struct Empty();

pub fn simple_post(path: String) -> Result<Response, Error> {
    post(path, &Empty {})
}

pub fn post<T>(path: String, payload: &T) -> Result<Response, Error>
where
    T: Serialize,
{
    let url = format!("{}{}", config::base_url(), path);
    debug!("Request URL: {}", url);

    let body = serde_json::to_string(&payload).unwrap();
    debug!("Request body: {}", body);

    let mut headers = Headers::new();
    headers.set(ContentType(
        Mime(TopLevel::Application, SubLevel::Json, vec![]),
    ));

    let client = Client::new();
    let mut builder = client.post(&url);
    builder = builder.headers(headers);
    builder = builder.body(&body[..]);
    builder.send()
}

pub fn extract_response(mut response: Response) -> (HttpStatus, String) {
    debug!("Response status: {}", response.status);
    let status = match response.status {
        StatusCode::Ok => HttpStatus::Ok,
        _ => HttpStatus::Other,
    };

    let mut body = String::new();
    response.read_to_string(&mut body).unwrap();;
    debug!("Response body: {}", body);

    (status, body)
}<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use appkit::NSRect;

use std::libc;
use std::libc::c_long;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub static NIL : id = 0 as id;

pub extern {
    fn class_addMethod(cls: Class, name: SEL, imp: IMP, types: *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[test]
pub fn test_nsapp() {
    let klass = str::as_c_str(~"NSApplication", |s|
        unsafe {
            objc_getClass(s)
        }
    );

    let sel = str::as_c_str(~"sharedApplication", |s|
        unsafe {
            sel_registerName(s)
        }
    );

    unsafe {
        let nsapp = objc_msgSend(klass, sel);
        io::println(fmt!("nsapp: %d", (nsapp as int)));
    }
}

#[test]
pub fn test_custom_obj() {
    extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
        io::println(~"doSomething");
        return this;
    }

    let NSObject = str::as_c_str(~"NSObject", |s|
        unsafe {
            objc_getClass(s)
        }
    );
    let MyObject = str::as_c_str(~"MyObject", |s|
        unsafe {
            objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
        }
    );
    let doSomething = str::as_c_str(~"doSomething", |s|
        unsafe {
            sel_registerName(s)
        }
    );
    let _ = str::as_c_str(~"@@:", |types|
        unsafe {
            class_addMethod(MyObject,
                                  doSomething,
                                  MyObject_doSomething,
                                  types)
        }
    );

    unsafe {
        objc_registerClassPair(MyObject);
    }

    let alloc = str::as_c_str(~"alloc", |s| unsafe { sel_registerName(s) });
    let init = str::as_c_str(~"init", |s| unsafe { sel_registerName(s) });

    unsafe {
        let mut obj = objc_msgSend(MyObject, alloc);
        obj = objc_msgSend(obj, init);
        objc_msgSend(obj, doSomething);
    }
}

/// Invokes the given selector, which must have the signature:
///
///     double f();
pub fn msg_send_double(theReceiver: id, theSelector: SEL) -> f64 {
    unsafe {
        invoke_msg_double(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f();
pub fn msg_send_id(theReceiver: id, theSelector: SEL) -> id {
    unsafe {
        invoke_msg_id(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f(NSRect a);
pub fn msg_send_id_NSRect(theReceiver: id, theSelector: SEL, a: NSRect) -> id {
    unsafe {
        invoke_msg_id_NSRect(theReceiver, theSelector, &a)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f(id a, id b, id c, id e, id f);
pub fn msg_send_id_id_id_id_id_id(theReceiver: id,
                                  theSelector: SEL,
                                  a: id,
                                  b: id,
                                  c: id,
                                  d: id,
                                  e: id)
                                  -> id {
    unsafe {
        invoke_msg_id_id_id_id_id_id(theReceiver, theSelector, a, b, c, d, e)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     long f();
pub fn msg_send_long(theReceiver: id, theSelector: SEL) -> c_long {
    unsafe {
        invoke_msg_long(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f();
pub fn msg_send_void(theReceiver: id, theSelector: SEL) {
    unsafe {
        invoke_msg_void(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f(BOOL a);
pub fn msg_send_void_bool(theReceiver: id, theSelector: SEL, a: bool) {
    unsafe {
        invoke_msg_void_bool(theReceiver, theSelector, a)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f(id a);
pub fn msg_send_void_id(theReceiver: id, theSelector: SEL, a: id) {
    unsafe {
        invoke_msg_void_id(theReceiver, theSelector, a)
    }
}

#[link_args = "-L. -lmsgsend"]
#[nolink]
extern {
    fn invoke_msg_double(theReceiver: id, theSelector: SEL) -> f64;
    fn invoke_msg_id(theReceiver: id, theSelector: SEL) -> id;
    fn invoke_msg_id_id_id_id_id_id(theReceiver: id,
                                    theSelector: SEL,
                                    a: id,
                                    b: id,
                                    c: id,
                                    d: id,
                                    e: id)
                                    -> id;
    fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id;
    fn invoke_msg_long(theReceiver: id, theSelector: SEL) -> c_long;
    fn invoke_msg_void(theReceiver: id, theSelector: SEL);
    fn invoke_msg_void_bool(theReceiver: id, theSelector: SEL, a: bool);
    fn invoke_msg_void_id(theReceiver: id, theSelector: SEL, a: id);
}

<commit_msg>Update tests for latest Rust.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use appkit::NSRect;

use std::libc;
use std::libc::c_long;

pub type id = libc::intptr_t;
pub type Class = libc::intptr_t;
pub type IMP = *u8;
pub type SEL = libc::intptr_t;
pub type Ivar = libc::intptr_t;

pub static NIL : id = 0 as id;

pub extern {
    fn class_addMethod(cls: Class, name: SEL, imp: IMP, types: *libc::c_char) -> bool;
    fn class_addIvar(cls : Class,
                     name : *libc::c_char,
                     size : libc::size_t,
                     alignment: u8,
		             types: *libc::c_char) -> bool;
    fn object_setInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  value : *libc::c_void);
    fn object_getInstanceVariable(obj : id,
                                  name : *libc::c_char,
                                  outValue : **libc::c_void);
    fn objc_allocateClassPair(superclass : Class,
                              name : *libc::c_char,
                              extraBytes : libc::size_t) -> Class;
    fn objc_getClass(name : *libc::c_char) -> id;
    fn objc_msgSend(theReceiver : id, theSelector : SEL) -> id;
    fn objc_registerClassPair(cls : Class);
    fn sel_registerName(name : *libc::c_char) -> SEL;
}

#[cfg(test)]
mod test {
    use std::{str,io,libc};
    use super::*;

    #[test]
    pub fn test_nsapp() {
        let klass = str::as_c_str("NSApplication", |s|
                                  unsafe {
                                      objc_getClass(s)
                                  }
                                 );

        let sel = str::as_c_str("sharedApplication", |s|
                                unsafe {
                                    sel_registerName(s)
                                }
                               );

        unsafe {
            let nsapp = objc_msgSend(klass, sel);
            io::println(fmt!("nsapp: %d", (nsapp as int)));
        }
    }

    #[test]
    pub fn test_custom_obj() {
        extern fn MyObject_doSomething(this : id, _sel : SEL) -> id {
            io::println("doSomething");
            return this;
        }

        let NSObject = str::as_c_str("NSObject", |s|
                                     unsafe {
                                         objc_getClass(s)
                                     }
                                    );
        let MyObject = str::as_c_str("MyObject", |s|
                                     unsafe {
                                         objc_allocateClassPair(NSObject, s, 0 as libc::size_t)
                                     }
                                    );
        let doSomething = str::as_c_str("doSomething", |s|
                                        unsafe {
                                            sel_registerName(s)
                                        }
                                       );
        let _ = str::as_c_str("@@:", |types|
                              unsafe {
                                  class_addMethod(MyObject,
                                                  doSomething,
                                                  MyObject_doSomething,
                                                  types)
                              }
                             );

        unsafe {
            objc_registerClassPair(MyObject);
        }

        let alloc = str::as_c_str("alloc", |s| unsafe { sel_registerName(s) });
        let init = str::as_c_str("init", |s| unsafe { sel_registerName(s) });

        unsafe {
            let mut obj = objc_msgSend(MyObject, alloc);
            obj = objc_msgSend(obj, init);
            objc_msgSend(obj, doSomething);
        }
    }
}

/// Invokes the given selector, which must have the signature:
///
///     double f();
pub fn msg_send_double(theReceiver: id, theSelector: SEL) -> f64 {
    unsafe {
        invoke_msg_double(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f();
pub fn msg_send_id(theReceiver: id, theSelector: SEL) -> id {
    unsafe {
        invoke_msg_id(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f(NSRect a);
pub fn msg_send_id_NSRect(theReceiver: id, theSelector: SEL, a: NSRect) -> id {
    unsafe {
        invoke_msg_id_NSRect(theReceiver, theSelector, &a)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     id f(id a, id b, id c, id e, id f);
pub fn msg_send_id_id_id_id_id_id(theReceiver: id,
                                  theSelector: SEL,
                                  a: id,
                                  b: id,
                                  c: id,
                                  d: id,
                                  e: id)
                                  -> id {
    unsafe {
        invoke_msg_id_id_id_id_id_id(theReceiver, theSelector, a, b, c, d, e)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     long f();
pub fn msg_send_long(theReceiver: id, theSelector: SEL) -> c_long {
    unsafe {
        invoke_msg_long(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f();
pub fn msg_send_void(theReceiver: id, theSelector: SEL) {
    unsafe {
        invoke_msg_void(theReceiver, theSelector)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f(BOOL a);
pub fn msg_send_void_bool(theReceiver: id, theSelector: SEL, a: bool) {
    unsafe {
        invoke_msg_void_bool(theReceiver, theSelector, a)
    }
}

/// Invokes the given selector, which must have the signature:
///
///     void f(id a);
pub fn msg_send_void_id(theReceiver: id, theSelector: SEL, a: id) {
    unsafe {
        invoke_msg_void_id(theReceiver, theSelector, a)
    }
}

#[link_args = "-L. -lmsgsend"]
#[nolink]
extern {
    fn invoke_msg_double(theReceiver: id, theSelector: SEL) -> f64;
    fn invoke_msg_id(theReceiver: id, theSelector: SEL) -> id;
    fn invoke_msg_id_id_id_id_id_id(theReceiver: id,
                                    theSelector: SEL,
                                    a: id,
                                    b: id,
                                    c: id,
                                    d: id,
                                    e: id)
                                    -> id;
    fn invoke_msg_id_NSRect(theReceiver: id, theSelector: SEL, a: &NSRect) -> id;
    fn invoke_msg_long(theReceiver: id, theSelector: SEL) -> c_long;
    fn invoke_msg_void(theReceiver: id, theSelector: SEL);
    fn invoke_msg_void_bool(theReceiver: id, theSelector: SEL, a: bool);
    fn invoke_msg_void_id(theReceiver: id, theSelector: SEL, a: id);
}

<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cast;
use std::libc;
use std::libc::c_long;

// a raw Core Foundation reference. It may or may not have been
// CFRetain'ed, depending on whether it was obtained via ownership or
// borrow semantics.
pub trait AbstractCFTypeRef {
    fn as_type_ref(&self) -> CFTypeRef;
    fn type_id(_dummy: Option<Self>) -> CFTypeID;
}

pub type Boolean = u8;

pub type CFIndex = c_long;
pub type CFOptionFlags = u32;
pub struct CFRange {
    location: CFIndex,
    length: CFIndex
}

pub fn CFRangeMake(off: CFIndex, len: CFIndex) -> CFRange {
    CFRange { location: off, length: len }
}

struct __CFAllocator { private: () }
pub type CFAllocatorRef = *__CFAllocator;

struct __CFNull { private: () }
pub type CFNullRef = *__CFNull;

pub type CFHashCode = libc::c_ulong;
pub type CFTypeID = libc::c_ulong;

struct __CFType { private: () }
pub type CFTypeRef = *__CFType;

impl AbstractCFTypeRef for CFTypeRef {
    fn as_type_ref(&self) -> CFTypeRef { *self }
    // this can't be used, because CFType is the supertype and has no type id.
    fn type_id(_dummy: Option<CFTypeRef>) -> CFTypeID { fail!(); }
}

#[fixed_stack_segment]
pub fn downcast<T:AbstractCFTypeRef>(r: CFTypeRef) -> T {
    unsafe {
        let dummy: Option<T> = None;
        assert!(CFGetTypeID(r) == AbstractCFTypeRef::type_id(dummy));
        cast::transmute::<CFTypeRef, T>(r)
    }
}

pub struct RawCFWrapper {
    obj: CFTypeRef
}

pub struct CFWrapper<T, PlaceholderType1, PlaceholderType2> {
    obj: T
}

#[unsafe_destructor]
impl<T,E1,E2> Drop for CFWrapper<T,E1,E2> {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            // sadly, cannot use obj.as_type_ref() here, because drop
            // cannot make virtual method calls using trait
            // types. Instead, just transmute the bugger.
            let this: &RawCFWrapper = cast::transmute::<&CFWrapper<T,E1,E2>, &RawCFWrapper>(self);
            assert!(CFGetRetainCount(this.obj) > 0 as CFIndex);
            CFRelease(this.obj)
        }
    }
}

pub type CFType = CFWrapper<CFTypeRef, (), ()>;

impl<'self, T:Clone + AbstractCFTypeRef, E1, E2> CFWrapper<T,E1,E2> {
    pub fn borrow_ref(&'self self) -> &'self T {
        &self.obj
    }

    pub fn borrow_type_ref(&self) -> &'self CFTypeRef {
        unsafe {
            cast::transmute::<&T, &CFTypeRef>(&self.obj)
        }
    }

    // Use this when following Core Foundation's "Create" rule; i.e., the wrapper assumes ownership.
    // The object has already been retained, so we need not increment the retain count ourself.
    pub fn wrap_owned(some_ref: T) -> CFWrapper<T,E1,E2> {
        // N.B. we can't make any assertions about retain count here,
        // because returned things are only guaranteed to be already
        // retained. Strings, for example, could be interned.
        CFWrapper { obj: some_ref }
    }

    // Use this when following Core Foundation's "Get" rule. The wrapper does not have ownership.
    // Twe need to increment object's the retain count so it isn't freed out from under our noses.
    #[fixed_stack_segment]
    pub fn wrap_shared(some_ref: T) -> CFWrapper<T,E1,E2> {
        unsafe { CFRetain(some_ref.as_type_ref()); }
        CFWrapper { obj: some_ref }
    }

    // Unwraps the wrapper, returning the underlying AbstractCFType.
    pub fn unwrap(wrapper: CFWrapper<T,E1,E2>) -> T {
        wrapper.obj.clone()
    }

    pub fn to_CFType(wrapper: CFWrapper<T,E1,E2>) -> CFType {
        unsafe {
            cast::transmute::<CFWrapper<T, E1, E2>, CFType>(wrapper)
        }
    }

    pub fn from_CFType(wrapper: CFType) -> CFWrapper<T,E1,E2> {
        unsafe {
            let dummy: Option<T> = None;
            assert!(wrapper.type_id() == AbstractCFTypeRef::type_id(dummy));
            cast::transmute::<CFType,CFWrapper<T,E1,E2>>(wrapper)
        }
    }

    pub fn clone(wrapper: &CFWrapper<T,E1,E2>) -> CFWrapper<T,E1,E2> {
        CFWrapper::wrap_shared((*wrapper.borrow_ref()).clone())
    }

    #[fixed_stack_segment]
    pub fn retain_count(&self) -> CFIndex {
        unsafe {
            CFGetRetainCount(*self.borrow_type_ref())
        }
    }

    #[fixed_stack_segment]
    pub fn type_id(&self) -> CFTypeID {
        unsafe {
            CFGetTypeID(*self.borrow_type_ref())
        }
    }

    #[fixed_stack_segment]
    pub fn show(&self) {
        unsafe {
            CFShow(*self.borrow_type_ref());
        }
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFBase.h
     */

    /* CFAllocator Reference */
    // N.B. Many CFAllocator functions and constants are omitted here.
    pub static kCFAllocatorDefault: CFAllocatorRef;
    pub static kCFAllocatorSystemDefault: CFAllocatorRef;
    pub static kCFAllocatorMalloc: CFAllocatorRef;
    pub static kCFAllocatorMallocZone: CFAllocatorRef;
    pub static kCFAllocatorNull: CFAllocatorRef;
    pub static kCFAllocatorUseContext: CFAllocatorRef;

    /* CFNull Reference */

    pub static kCFNull: CFNullRef;

    /* CFType Reference */

    //fn CFCopyDescription
    //fn CFCopyTypeIDDescription
    //fn CFEqual
    //fn CFGetAllocator
    pub fn CFGetRetainCount(cf: CFTypeRef) -> CFIndex;
    pub fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
    pub fn CFHash(cf: CFTypeRef) -> CFHashCode;
    //fn CFMakeCollectable
    pub fn CFRelease(cf: CFTypeRef);
    pub fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
    pub fn CFShow(obj: CFTypeRef);

    /* Base Utilities Reference */
    // N.B. Some things missing here.
}
<commit_msg>Add a FIXME comment for the dummy parameter work around. See #17<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cast;
use std::libc;
use std::libc::c_long;

// a raw Core Foundation reference. It may or may not have been
// CFRetain'ed, depending on whether it was obtained via ownership or
// borrow semantics.
pub trait AbstractCFTypeRef {
    fn as_type_ref(&self) -> CFTypeRef;

    // FIXME https://github.com/mozilla-servo/rust-core-foundation/issues/17
    fn type_id(_dummy: Option<Self>) -> CFTypeID;
}

pub type Boolean = u8;

pub type CFIndex = c_long;
pub type CFOptionFlags = u32;
pub struct CFRange {
    location: CFIndex,
    length: CFIndex
}

pub fn CFRangeMake(off: CFIndex, len: CFIndex) -> CFRange {
    CFRange { location: off, length: len }
}

struct __CFAllocator { private: () }
pub type CFAllocatorRef = *__CFAllocator;

struct __CFNull { private: () }
pub type CFNullRef = *__CFNull;

pub type CFHashCode = libc::c_ulong;
pub type CFTypeID = libc::c_ulong;

struct __CFType { private: () }
pub type CFTypeRef = *__CFType;

impl AbstractCFTypeRef for CFTypeRef {
    fn as_type_ref(&self) -> CFTypeRef { *self }
    // this can't be used, because CFType is the supertype and has no type id.
    fn type_id(_dummy: Option<CFTypeRef>) -> CFTypeID { fail!(); }
}

#[fixed_stack_segment]
pub fn downcast<T:AbstractCFTypeRef>(r: CFTypeRef) -> T {
    unsafe {
        let dummy: Option<T> = None;
        assert!(CFGetTypeID(r) == AbstractCFTypeRef::type_id(dummy));
        cast::transmute::<CFTypeRef, T>(r)
    }
}

pub struct RawCFWrapper {
    obj: CFTypeRef
}

pub struct CFWrapper<T, PlaceholderType1, PlaceholderType2> {
    obj: T
}

#[unsafe_destructor]
impl<T,E1,E2> Drop for CFWrapper<T,E1,E2> {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            // sadly, cannot use obj.as_type_ref() here, because drop
            // cannot make virtual method calls using trait
            // types. Instead, just transmute the bugger.
            let this: &RawCFWrapper = cast::transmute::<&CFWrapper<T,E1,E2>, &RawCFWrapper>(self);
            assert!(CFGetRetainCount(this.obj) > 0 as CFIndex);
            CFRelease(this.obj)
        }
    }
}

pub type CFType = CFWrapper<CFTypeRef, (), ()>;

impl<'self, T:Clone + AbstractCFTypeRef, E1, E2> CFWrapper<T,E1,E2> {
    pub fn borrow_ref(&'self self) -> &'self T {
        &self.obj
    }

    pub fn borrow_type_ref(&self) -> &'self CFTypeRef {
        unsafe {
            cast::transmute::<&T, &CFTypeRef>(&self.obj)
        }
    }

    // Use this when following Core Foundation's "Create" rule; i.e., the wrapper assumes ownership.
    // The object has already been retained, so we need not increment the retain count ourself.
    pub fn wrap_owned(some_ref: T) -> CFWrapper<T,E1,E2> {
        // N.B. we can't make any assertions about retain count here,
        // because returned things are only guaranteed to be already
        // retained. Strings, for example, could be interned.
        CFWrapper { obj: some_ref }
    }

    // Use this when following Core Foundation's "Get" rule. The wrapper does not have ownership.
    // Twe need to increment object's the retain count so it isn't freed out from under our noses.
    #[fixed_stack_segment]
    pub fn wrap_shared(some_ref: T) -> CFWrapper<T,E1,E2> {
        unsafe { CFRetain(some_ref.as_type_ref()); }
        CFWrapper { obj: some_ref }
    }

    // Unwraps the wrapper, returning the underlying AbstractCFType.
    pub fn unwrap(wrapper: CFWrapper<T,E1,E2>) -> T {
        wrapper.obj.clone()
    }

    pub fn to_CFType(wrapper: CFWrapper<T,E1,E2>) -> CFType {
        unsafe {
            cast::transmute::<CFWrapper<T, E1, E2>, CFType>(wrapper)
        }
    }

    pub fn from_CFType(wrapper: CFType) -> CFWrapper<T,E1,E2> {
        unsafe {
            let dummy: Option<T> = None;
            assert!(wrapper.type_id() == AbstractCFTypeRef::type_id(dummy));
            cast::transmute::<CFType,CFWrapper<T,E1,E2>>(wrapper)
        }
    }

    pub fn clone(wrapper: &CFWrapper<T,E1,E2>) -> CFWrapper<T,E1,E2> {
        CFWrapper::wrap_shared((*wrapper.borrow_ref()).clone())
    }

    #[fixed_stack_segment]
    pub fn retain_count(&self) -> CFIndex {
        unsafe {
            CFGetRetainCount(*self.borrow_type_ref())
        }
    }

    #[fixed_stack_segment]
    pub fn type_id(&self) -> CFTypeID {
        unsafe {
            CFGetTypeID(*self.borrow_type_ref())
        }
    }

    #[fixed_stack_segment]
    pub fn show(&self) {
        unsafe {
            CFShow(*self.borrow_type_ref());
        }
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFBase.h
     */

    /* CFAllocator Reference */
    // N.B. Many CFAllocator functions and constants are omitted here.
    pub static kCFAllocatorDefault: CFAllocatorRef;
    pub static kCFAllocatorSystemDefault: CFAllocatorRef;
    pub static kCFAllocatorMalloc: CFAllocatorRef;
    pub static kCFAllocatorMallocZone: CFAllocatorRef;
    pub static kCFAllocatorNull: CFAllocatorRef;
    pub static kCFAllocatorUseContext: CFAllocatorRef;

    /* CFNull Reference */

    pub static kCFNull: CFNullRef;

    /* CFType Reference */

    //fn CFCopyDescription
    //fn CFCopyTypeIDDescription
    //fn CFEqual
    //fn CFGetAllocator
    pub fn CFGetRetainCount(cf: CFTypeRef) -> CFIndex;
    pub fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
    pub fn CFHash(cf: CFTypeRef) -> CFHashCode;
    //fn CFMakeCollectable
    pub fn CFRelease(cf: CFTypeRef);
    pub fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
    pub fn CFShow(obj: CFTypeRef);

    /* Base Utilities Reference */
    // N.B. Some things missing here.
}
<|endoftext|>