<commit_before>use std::str;
use std::collections::{HashMap, BTreeSet};

use rocksdb::{self, Writable, IteratorMode, Direction, WriteBatch};
use byteorder::{ByteOrder, BigEndian, WriteBytesExt};

use RocksDBIndexStore;
use document_index::DocRef;
use key_builder::KeyBuilder;
use doc_id_set::DocIdSet;

#[derive(Debug)]
pub enum SegmentMergeError {
    TooManyDocs,
    RocksDBError(rocksdb::Error),
}

impl From<rocksdb::Error> for SegmentMergeError {
    fn from(e: rocksdb::Error) -> SegmentMergeError {
        SegmentMergeError::RocksDBError(e)
    }
}

impl RocksDBIndexStore {
    fn merge_segment_data(&self, source_segments: &Vec<u32>, dest_segment: u32, doc_ref_mapping: &HashMap<DocRef, u16>) -> Result<(), SegmentMergeError> {
        // Put source_segments in a BTreeSet as this is much faster for performing contains queries against
        let source_segments_btree = source_segments.iter().collect::<BTreeSet<_>>();

        // Merge the term directories
        // The term directory keys are ordered to be most convenient for retrieving all the segments
        // of for a term/field combination in one go (field/term/segment). So we don't end up pulling
        // in a lot of unwanted data, we firstly iterate the keys, it they one of the source segments
        // looking for then we load them and append them to our new segment.

        /// Converts term directory key strings "d1/2/3" into tuples of 3 i32s (1, 2, 3)
        fn parse_term_directory_key(key: &[u8]) -> (u32, u32, u32) {
            let mut nums_iter = key[1..].split(|b| *b == b'/').map(|s| str::from_utf8(s).unwrap().parse::<u32>().unwrap());
            (nums_iter.next().unwrap(), nums_iter.next().unwrap(), nums_iter.next().unwrap())
        }

        let mut current_td_key: Option<(u32, u32)> = None;
        let mut current_td = Vec::new();

        for (k, v) in self.db.iterator(IteratorMode::From(b"d", Direction::Forward)) {
            if k[0] != b'd' {
                // No more term directories to merge
                break;
            }

            let (field, term, segment) = parse_term_directory_key(&k);

            if !source_segments_btree.contains(&segment) {
                continue;
            }

            if current_td_key != Some((field, term)) {
                // Finished current term directory. Write it to the DB and start the next one
                if let Some((field, term)) = current_td_key {
                    let kb = KeyBuilder::segment_dir_list(dest_segment, field, term);
                    try!(self.db.put(&kb.key(), &current_td));
                    current_td.clear();
                }

                current_td_key = Some((field, term));
            }

            // Merge term directory into the new one (and remap the doc ids)
            for doc_id in DocIdSet::Owned(v.to_vec()).iter() {
                let doc_ref = DocRef::from_segment_ord(segment, doc_id);
                let new_doc_id = doc_ref_mapping.get(&doc_ref).unwrap();
                current_td.write_u16::<BigEndian>(*new_doc_id).unwrap();
            }
        }

        // All done, write the last term directory
        if let Some((field, term)) = current_td_key {
            let kb = KeyBuilder::segment_dir_list(dest_segment, field, term);
            try!(self.db.put(&kb.key(), &current_td));
            current_td.clear();
        }

        // Merge the stored values
        // All stored value keys start with the segment id. So we need to:
        // - Iterate all stored value keys that are prefixed by one of the stored segment ids
        // - Remap their doc ids to the one in the new segment
        // - Write the value back with the new segment/doc ids in the key

        /// Converts stored value key strings "v1/2/3/v" into tuples of 3 i32s and a Vec<u8> (1, 2, 3, vec![b'v', b'a', b'l'])
        fn parse_stored_value_key(key: &[u8]) -> (u32, u32, u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let doc_id = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let field_ord = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let value_type = parts_iter.next().unwrap().to_vec();

            (segment, doc_id, field_ord, value_type)
        }

        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stored_values_prefix(*source_segment);
            for (k, v) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b'v' {
                    // No more stored values to move
                    break;
                }

                let (segment, doc_id, field, value_type) = parse_stored_value_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                // Remap doc id
                let doc_ref = DocRef::from_segment_ord(segment, doc_id as u16);
                let new_doc_id = doc_ref_mapping.get(&doc_ref).unwrap();

                // Write value into new segment
                let kb = KeyBuilder::stored_field_value(dest_segment, *new_doc_id, field, &value_type);
                try!(self.db.put(&kb.key(), &v));
            }
        }

        // Merge the statistics
        // Like stored values, these start with segment ids. But instead of just rewriting the
        // key, we need to sum up all the statistics across the segments being merged.

        let mut statistics = HashMap::new();

        /// Converts statistic key strings "s1/total_docs" into tuples of 1 i32 and a Vec<u8> (1, ['t', 'o', 't', ...])
        fn parse_statistic_key(key: &[u8]) -> (u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let statistic_name = parts_iter.next().unwrap().to_vec();

            (segment, statistic_name)
        }

        // Fetch and merge statistics
        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stat_prefix(*source_segment);
            for (k, v) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b's' {
                    // No more statistics to merge
                    break;
                }

                let (segment, statistic_name) = parse_statistic_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                let mut stat = statistics.entry(statistic_name).or_insert(0);
                *stat += BigEndian::read_i64(&v);
            }
        }

        // Write merged statistics to new segment
        for (stat_name, stat_value) in statistics {
            let kb = KeyBuilder::segment_stat(dest_segment, &stat_name);
            let mut val_bytes = [0; 8];
            BigEndian::write_i64(&mut val_bytes, stat_value);
            try!(self.db.put(&kb.key(), &val_bytes));
        }

        // Note: Don't merge the deletion lists
        // Deletion lists can change at any time so we must lock the "document index"
        // before merging them so they can't be altered during merge. we cannot lock
        // this until the commit phase though.

        Ok(())
    }

    fn commit_segment_merge(&self, source_segments: &Vec<u32>, dest_segment: u32, doc_ref_mapping: &HashMap<DocRef, u16>) -> Result<(), SegmentMergeError> {
        let write_batch = WriteBatch::default();

        // Activate new segment
        let kb = KeyBuilder::segment_active(dest_segment);
        try!(write_batch.put(&kb.key(), b""));

        // Deactivate old segments
        for source_segment in source_segments.iter() {
            // Activate new segment
            let kb = KeyBuilder::segment_active(*source_segment);
            try!(write_batch.delete(&kb.key()));
        }

        // Update document index and commit
        // This will write the write batch
        try!(self.document_index.commit_segment_merge(&self.db, write_batch, source_segments, dest_segment, doc_ref_mapping));

        Ok(())
    }

    pub fn merge_segments(&self, source_segments: &Vec<u32>) -> Result<u32, SegmentMergeError> {
        let dest_segment = try!(self.segments.new_segment(&self.db));

        // Generate a mapping between the ids of the documents in the old segments to the new one
        // This packs the id spaces of the old segments together:
        // For example, say we have to merge 3 segments with 100 documents each:
        //  - The first segment's ids will be the same as before
        //  - The second segment's ids will be remapped to 100 - 199
        //  - The third segment's ids will be remapped to 200 - 299

        let mut doc_ref_mapping: HashMap<DocRef, u16> = HashMap::new();
        let mut current_ord: u32 = 0;

        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stat(*source_segment, b"total_docs");
            let total_docs = match try!(self.db.get(&kb.key())) {
                Some(total_docs_bytes) => {
                    BigEndian::read_i64(&total_docs_bytes)
                }
                None => continue,
            };

            for source_ord in 0..total_docs {
                if current_ord >= 65536 {
                    return Err(SegmentMergeError::TooManyDocs);
                }

                let from = DocRef::from_segment_ord(*source_segment, source_ord as u16);
                doc_ref_mapping.insert(from, current_ord as u16);
                current_ord += 1;
            }
        }

        // Merge segment data
        // Most of the heavy lifting happens here. This merges all the immutable parts of
        // the segment (which is everything but the deletion list). It does not activate the
        // segment.
        // This means that nothing bad will happen if it crashes half way through -- the
        // worst that could happen is we're left with a partially-written segment that we
        // have to clean up.
        try!(self.merge_segment_data(&source_segments, dest_segment, &doc_ref_mapping));

        // Commit the merge
        // This activates the new segment and updates the document index. Effectively committing
        // the merge.
        // Throughout this stage we need an exclusive lock to the document index. This is to
        // prevent documents in the source segments being deleted/updated so we don't accidentally
        // undelete them (this will block until the merge is complete so they delete/update from
        // the new segment).
        try!(self.commit_segment_merge(&source_segments, dest_segment, &doc_ref_mapping));

        Ok(dest_segment)
    }

    pub fn purge_segments(&self, segments: &Vec<u32>) -> Result<(), rocksdb::Error> {
        // Put segments in a BTreeSet as this is much faster for performing contains queries against
        let segments_btree = segments.iter().collect::<BTreeSet<_>>();

        // Purge term directories

        /// Converts term directory key strings "d1/2/3" into tuples of 3 i32s (1, 2, 3)
        fn parse_term_directory_key(key: &[u8]) -> (u32, u32, u32) {
            let mut nums_iter = key[1..].split(|b| *b == b'/').map(|s| str::from_utf8(s).unwrap().parse::<u32>().unwrap());
            (nums_iter.next().unwrap(), nums_iter.next().unwrap(), nums_iter.next().unwrap())
        }

        for (k, _) in self.db.iterator(IteratorMode::From(b"d", Direction::Forward)) {
            if k[0] != b'd' {
                // No more term directories to delete
                break;
            }

            let (_, _, segment) = parse_term_directory_key(&k);

            if segments_btree.contains(&segment) {
                try!(self.db.delete(&k));
            }
        }

        // Purge the stored values

        /// Converts stored value key strings "v1/2/3/v" into tuples of 3 i32s and a Vec<u8> (1, 2, 3, vec![b'v', b'a', b'l'])
        fn parse_stored_value_key(key: &[u8]) -> (u32, u32, u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let doc_id = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let field_ord = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let value_type = parts_iter.next().unwrap().to_vec();

            (segment, doc_id, field_ord, value_type)
        }

        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_stored_values_prefix(*source_segment);
            for (k, _) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b'v' {
                    // No more stored values to delete
                    break;
                }

                let (segment, _, _, _) = parse_stored_value_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                try!(self.db.delete(&k));
            }
        }

        // Purge the statistics

        /// Converts statistic key strings "s1/total_docs" into tuples of 1 i32 and a Vec<u8> (1, ['t', 'o', 't', ...])
        fn parse_statistic_key(key: &[u8]) -> (u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let statistic_name = parts_iter.next().unwrap().to_vec();

            (segment, statistic_name)
        }

        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_stat_prefix(*source_segment);
            for (k, _) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b's' {
                    // No more statistics to purge
                    break;
                }

                let (segment, _) = parse_statistic_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                try!(self.db.delete(&k));
            }
        }

        // Purge the deletion lists
        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_del_list(*source_segment);
            try!(self.db.delete(&kb.key()));
        }

        Ok(())
    }
}
<commit_msg>Make SegmentMergeError convertable to String<commit_after>use std::str;
use std::collections::{HashMap, BTreeSet};

use rocksdb::{self, Writable, IteratorMode, Direction, WriteBatch};
use byteorder::{ByteOrder, BigEndian, WriteBytesExt};

use RocksDBIndexStore;
use document_index::DocRef;
use key_builder::KeyBuilder;
use doc_id_set::DocIdSet;

#[derive(Debug)]
pub enum SegmentMergeError {
    TooManyDocs,
    RocksDBError(rocksdb::Error),
}

impl From<rocksdb::Error> for SegmentMergeError {
    fn from(e: rocksdb::Error) -> SegmentMergeError {
        SegmentMergeError::RocksDBError(e)
    }
}

impl From<SegmentMergeError> for String {
    fn from(e: SegmentMergeError) -> String {
        match e {
            SegmentMergeError::TooManyDocs => "Too many docs".to_string(),
            SegmentMergeError::RocksDBError(e) => e.into(),
        }
    }
}

impl RocksDBIndexStore {
    fn merge_segment_data(&self, source_segments: &Vec<u32>, dest_segment: u32, doc_ref_mapping: &HashMap<DocRef, u16>) -> Result<(), SegmentMergeError> {
        // Put source_segments in a BTreeSet as this is much faster for performing contains queries against
        let source_segments_btree = source_segments.iter().collect::<BTreeSet<_>>();

        // Merge the term directories
        // The term directory keys are ordered to be most convenient for retrieving all the segments
        // of for a term/field combination in one go (field/term/segment). So we don't end up pulling
        // in a lot of unwanted data, we firstly iterate the keys, it they one of the source segments
        // looking for then we load them and append them to our new segment.

        /// Converts term directory key strings "d1/2/3" into tuples of 3 i32s (1, 2, 3)
        fn parse_term_directory_key(key: &[u8]) -> (u32, u32, u32) {
            let mut nums_iter = key[1..].split(|b| *b == b'/').map(|s| str::from_utf8(s).unwrap().parse::<u32>().unwrap());
            (nums_iter.next().unwrap(), nums_iter.next().unwrap(), nums_iter.next().unwrap())
        }

        let mut current_td_key: Option<(u32, u32)> = None;
        let mut current_td = Vec::new();

        for (k, v) in self.db.iterator(IteratorMode::From(b"d", Direction::Forward)) {
            if k[0] != b'd' {
                // No more term directories to merge
                break;
            }

            let (field, term, segment) = parse_term_directory_key(&k);

            if !source_segments_btree.contains(&segment) {
                continue;
            }

            if current_td_key != Some((field, term)) {
                // Finished current term directory. Write it to the DB and start the next one
                if let Some((field, term)) = current_td_key {
                    let kb = KeyBuilder::segment_dir_list(dest_segment, field, term);
                    try!(self.db.put(&kb.key(), &current_td));
                    current_td.clear();
                }

                current_td_key = Some((field, term));
            }

            // Merge term directory into the new one (and remap the doc ids)
            for doc_id in DocIdSet::Owned(v.to_vec()).iter() {
                let doc_ref = DocRef::from_segment_ord(segment, doc_id);
                let new_doc_id = doc_ref_mapping.get(&doc_ref).unwrap();
                current_td.write_u16::<BigEndian>(*new_doc_id).unwrap();
            }
        }

        // All done, write the last term directory
        if let Some((field, term)) = current_td_key {
            let kb = KeyBuilder::segment_dir_list(dest_segment, field, term);
            try!(self.db.put(&kb.key(), &current_td));
            current_td.clear();
        }

        // Merge the stored values
        // All stored value keys start with the segment id. So we need to:
        // - Iterate all stored value keys that are prefixed by one of the stored segment ids
        // - Remap their doc ids to the one in the new segment
        // - Write the value back with the new segment/doc ids in the key

        /// Converts stored value key strings "v1/2/3/v" into tuples of 3 i32s and a Vec<u8> (1, 2, 3, vec![b'v', b'a', b'l'])
        fn parse_stored_value_key(key: &[u8]) -> (u32, u32, u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let doc_id = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let field_ord = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let value_type = parts_iter.next().unwrap().to_vec();

            (segment, doc_id, field_ord, value_type)
        }

        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stored_values_prefix(*source_segment);
            for (k, v) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b'v' {
                    // No more stored values to move
                    break;
                }

                let (segment, doc_id, field, value_type) = parse_stored_value_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                // Remap doc id
                let doc_ref = DocRef::from_segment_ord(segment, doc_id as u16);
                let new_doc_id = doc_ref_mapping.get(&doc_ref).unwrap();

                // Write value into new segment
                let kb = KeyBuilder::stored_field_value(dest_segment, *new_doc_id, field, &value_type);
                try!(self.db.put(&kb.key(), &v));
            }
        }

        // Merge the statistics
        // Like stored values, these start with segment ids. But instead of just rewriting the
        // key, we need to sum up all the statistics across the segments being merged.

        let mut statistics = HashMap::new();

        /// Converts statistic key strings "s1/total_docs" into tuples of 1 i32 and a Vec<u8> (1, ['t', 'o', 't', ...])
        fn parse_statistic_key(key: &[u8]) -> (u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let statistic_name = parts_iter.next().unwrap().to_vec();

            (segment, statistic_name)
        }

        // Fetch and merge statistics
        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stat_prefix(*source_segment);
            for (k, v) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b's' {
                    // No more statistics to merge
                    break;
                }

                let (segment, statistic_name) = parse_statistic_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                let mut stat = statistics.entry(statistic_name).or_insert(0);
                *stat += BigEndian::read_i64(&v);
            }
        }

        // Write merged statistics to new segment
        for (stat_name, stat_value) in statistics {
            let kb = KeyBuilder::segment_stat(dest_segment, &stat_name);
            let mut val_bytes = [0; 8];
            BigEndian::write_i64(&mut val_bytes, stat_value);
            try!(self.db.put(&kb.key(), &val_bytes));
        }

        // Note: Don't merge the deletion lists
        // Deletion lists can change at any time so we must lock the "document index"
        // before merging them so they can't be altered during merge. we cannot lock
        // this until the commit phase though.

        Ok(())
    }

    fn commit_segment_merge(&self, source_segments: &Vec<u32>, dest_segment: u32, doc_ref_mapping: &HashMap<DocRef, u16>) -> Result<(), SegmentMergeError> {
        let write_batch = WriteBatch::default();

        // Activate new segment
        let kb = KeyBuilder::segment_active(dest_segment);
        try!(write_batch.put(&kb.key(), b""));

        // Deactivate old segments
        for source_segment in source_segments.iter() {
            // Activate new segment
            let kb = KeyBuilder::segment_active(*source_segment);
            try!(write_batch.delete(&kb.key()));
        }

        // Update document index and commit
        // This will write the write batch
        try!(self.document_index.commit_segment_merge(&self.db, write_batch, source_segments, dest_segment, doc_ref_mapping));

        Ok(())
    }

    pub fn merge_segments(&self, source_segments: &Vec<u32>) -> Result<u32, SegmentMergeError> {
        let dest_segment = try!(self.segments.new_segment(&self.db));

        // Generate a mapping between the ids of the documents in the old segments to the new one
        // This packs the id spaces of the old segments together:
        // For example, say we have to merge 3 segments with 100 documents each:
        //  - The first segment's ids will be the same as before
        //  - The second segment's ids will be remapped to 100 - 199
        //  - The third segment's ids will be remapped to 200 - 299

        let mut doc_ref_mapping: HashMap<DocRef, u16> = HashMap::new();
        let mut current_ord: u32 = 0;

        for source_segment in source_segments.iter() {
            let kb = KeyBuilder::segment_stat(*source_segment, b"total_docs");
            let total_docs = match try!(self.db.get(&kb.key())) {
                Some(total_docs_bytes) => {
                    BigEndian::read_i64(&total_docs_bytes)
                }
                None => continue,
            };

            for source_ord in 0..total_docs {
                if current_ord >= 65536 {
                    return Err(SegmentMergeError::TooManyDocs);
                }

                let from = DocRef::from_segment_ord(*source_segment, source_ord as u16);
                doc_ref_mapping.insert(from, current_ord as u16);
                current_ord += 1;
            }
        }

        // Merge segment data
        // Most of the heavy lifting happens here. This merges all the immutable parts of
        // the segment (which is everything but the deletion list). It does not activate the
        // segment.
        // This means that nothing bad will happen if it crashes half way through -- the
        // worst that could happen is we're left with a partially-written segment that we
        // have to clean up.
        try!(self.merge_segment_data(&source_segments, dest_segment, &doc_ref_mapping));

        // Commit the merge
        // This activates the new segment and updates the document index. Effectively committing
        // the merge.
        // Throughout this stage we need an exclusive lock to the document index. This is to
        // prevent documents in the source segments being deleted/updated so we don't accidentally
        // undelete them (this will block until the merge is complete so they delete/update from
        // the new segment).
        try!(self.commit_segment_merge(&source_segments, dest_segment, &doc_ref_mapping));

        Ok(dest_segment)
    }

    pub fn purge_segments(&self, segments: &Vec<u32>) -> Result<(), rocksdb::Error> {
        // Put segments in a BTreeSet as this is much faster for performing contains queries against
        let segments_btree = segments.iter().collect::<BTreeSet<_>>();

        // Purge term directories

        /// Converts term directory key strings "d1/2/3" into tuples of 3 i32s (1, 2, 3)
        fn parse_term_directory_key(key: &[u8]) -> (u32, u32, u32) {
            let mut nums_iter = key[1..].split(|b| *b == b'/').map(|s| str::from_utf8(s).unwrap().parse::<u32>().unwrap());
            (nums_iter.next().unwrap(), nums_iter.next().unwrap(), nums_iter.next().unwrap())
        }

        for (k, _) in self.db.iterator(IteratorMode::From(b"d", Direction::Forward)) {
            if k[0] != b'd' {
                // No more term directories to delete
                break;
            }

            let (_, _, segment) = parse_term_directory_key(&k);

            if segments_btree.contains(&segment) {
                try!(self.db.delete(&k));
            }
        }

        // Purge the stored values

        /// Converts stored value key strings "v1/2/3/v" into tuples of 3 i32s and a Vec<u8> (1, 2, 3, vec![b'v', b'a', b'l'])
        fn parse_stored_value_key(key: &[u8]) -> (u32, u32, u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let doc_id = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let field_ord = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let value_type = parts_iter.next().unwrap().to_vec();

            (segment, doc_id, field_ord, value_type)
        }

        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_stored_values_prefix(*source_segment);
            for (k, _) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b'v' {
                    // No more stored values to delete
                    break;
                }

                let (segment, _, _, _) = parse_stored_value_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                try!(self.db.delete(&k));
            }
        }

        // Purge the statistics

        /// Converts statistic key strings "s1/total_docs" into tuples of 1 i32 and a Vec<u8> (1, ['t', 'o', 't', ...])
        fn parse_statistic_key(key: &[u8]) -> (u32, Vec<u8>) {
            let mut parts_iter = key[1..].split(|b| *b == b'/');
            let segment = str::from_utf8(parts_iter.next().unwrap()).unwrap().parse::<u32>().unwrap();
            let statistic_name = parts_iter.next().unwrap().to_vec();

            (segment, statistic_name)
        }

        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_stat_prefix(*source_segment);
            for (k, _) in self.db.iterator(IteratorMode::From(&kb.key(), Direction::Forward)) {
                if k[0] != b's' {
                    // No more statistics to purge
                    break;
                }

                let (segment, _) = parse_statistic_key(&k);

                if segment != *source_segment {
                    // Segment finished
                    break;
                }

                try!(self.db.delete(&k));
            }
        }

        // Purge the deletion lists
        for source_segment in segments.iter() {
            let kb = KeyBuilder::segment_del_list(*source_segment);
            try!(self.db.delete(&kb.key()));
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Venkata-Gautam Kanumuru (vrk7bp)
//
// zhttpto.rs
//
// Final code for PS1
// Running on Rust 0.9
//
// Note that this code has serious security risks!  You should not run it
// on any system with access to sensitive files.
//
// University of Virginia - cs4414 Spring 2014
// Originally coded by Weilin Xu and David Evans
// Version 0.3

#[feature(globs)];
use std::io::*;
use std::io::net::ip::{SocketAddr};
use std::{str};

static IP: &'static str = "127.0.0.1";
static PORT:        int = 4414;
static mut visitCount: int = 0;

fn main() {
    let addr = from_str::<SocketAddr>(format!("{:s}:{:d}", IP, PORT)).unwrap();
    let mut acceptor = net::tcp::TcpListener::bind(addr).listen();

    println(format!("Listening on [{:s}] ...", addr.to_str()));

    for stream in acceptor.incoming() {

        // Spawn a task to handle the connection
        do spawn {
            let mut stream = stream;

            match stream {
                Some(ref mut s) => {
                             match s.peer_name() {
                                Some(pn) => {println(format!("Received connection from: [{:s}]", pn.to_str()));},
                                None => ()
                             }
                           },
                None => ()
            }

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            //Gets the contents of the 2nd /, so as to check if it is an empty path or not.
            let splitArray: ~[&str] = request_str.split(' ').collect();
            let path = splitArray[1].slice_from(1).to_owned();
            let testVal = ~"";
            println!("{}", path);

            //Obtain, potentially, the last four characters in the string to check if they are html
            let borrowedPath = path.clone();
            let size = path.len();
            let HTMLTestVal = ~"html";
            let mut isHTML = ~"N/A";
            if(size > 4) {
               isHTML = borrowedPath.slice_from(size-4).to_owned();
            };

            println(format!("Received request :\n{:s}", request_str));

            //First check if there is no directories being accessed. If so then show the original "home screen".
            if (std::str::eq(&path, &testVal)) {
                let response: ~str =
                ~"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                 <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

                 \r\n";
                stream.write(response.as_bytes());
            }

            //Second, check to see if the last four characters in the path are .html. If so, then there are two possible scenarios...
            else if(std::str::eq(&isHTML, &HTMLTestVal)) {
                let tempPath = path.clone();
                let filePath = Path::new(path);

                //The path/document is valid and the particular file will be shown through the browser.
                if(filePath.exists()) {
                    let mut msg_file = File::open(&filePath);
                    let msg_bytes: ~[u8] = msg_file.read_to_end();
                    stream.write(msg_bytes);
                }

                //Otherwise the path/document is not valid and the 403 page will be brought up with the relevant path.
                else {
                    let response: ~str =
                    format!("HTTP/1.1 403 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                    <doctype !html><title>403 Forbidden</title>

 Forbidden

 You don't have permission to access /{:s}

                    \r\n", tempPath);
                    stream.write(response.as_bytes());
                };
            }

            //If none of the above two situations were "met", then show the 403 page with the relevant path.
            else {
                let response: ~str =
                format!("HTTP/1.1 403 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                 <doctype !html><title>403 Forbidden</title>

 Forbidden

 You don't have permission to access /{:s}

                 \r\n", path);
                stream.write(response.as_bytes());
            };

            //In the command prompt, print out "Connection terminates." as well as the new page visited number.
            println!("Connection terminates.");
            unsafe {
                visitCount = visitCount + 1;
                println!("This page has been called {} time(s).", visitCount);
            }
        }
    }
}
<commit_msg>Includes adding refreshes to Web Page.<commit_after>// Venkata-Gautam Kanumuru (vrk7bp)
//
// zhttpto.rs
//
// Final code for PS1
// Running on Rust 0.9
//
// Note that this code has serious security risks!  You should not run it
// on any system with access to sensitive files.
//
// University of Virginia - cs4414 Spring 2014
// Originally coded by Weilin Xu and David Evans
// Version 0.3

#[feature(globs)];
use std::io::*;
use std::io::net::ip::{SocketAddr};
use std::{str};

static IP: &'static str = "127.0.0.1";
static PORT:        int = 4414;
static mut visitCount: int = 0;

fn main() {
    let addr = from_str::<SocketAddr>(format!("{:s}:{:d}", IP, PORT)).unwrap();
    let mut acceptor = net::tcp::TcpListener::bind(addr).listen();

    println(format!("Listening on [{:s}] ...", addr.to_str()));

    for stream in acceptor.incoming() {

        // Spawn a task to handle the connection
        do spawn {
            let mut stream = stream;

            match stream {
                Some(ref mut s) => {
                             match s.peer_name() {
                                Some(pn) => {println(format!("Received connection from: [{:s}]", pn.to_str()));},
                                None => ()
                             }
                           },
                None => ()
            }

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            //Gets the contents of the 2nd /, so as to check if it is an empty path or not.
            let splitArray: ~[&str] = request_str.split(' ').collect();
            let path = splitArray[1].slice_from(1).to_owned();
            let testVal = ~"";
            unsafe {
                visitCount = visitCount + 1;
            }

            //Obtain, potentially, the last four characters in the string to check if they are html
            let borrowedPath = path.clone();
            let size = path.len();
            let HTMLTestVal = ~"html";
            let mut isHTML = ~"N/A";
            if(size > 4) {
               isHTML = borrowedPath.slice_from(size-4).to_owned();
            };

            println(format!("Received request :\n{:s}", request_str));

            //First check if there is no directories being accessed. If so then show the original "home screen".
            if (std::str::eq(&path, &testVal)) {
                let response: ~str =
                format!("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                 <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Total Server Refreshes: {:d}

                 \r\n", unsafe{visitCount});
                stream.write(response.as_bytes());
            }

            //Second, check to see if the last four characters in the path are .html. If so, then there are two possible scenarios...
            else if(std::str::eq(&isHTML, &HTMLTestVal)) {
                let tempPath = path.clone();
                let filePath = Path::new(path);

                //The path/document is valid and the particular file will be shown through the browser.
                if(filePath.exists()) {
                    let mut msg_file = File::open(&filePath);
                    let msg_bytes: ~[u8] = msg_file.read_to_end();
                    stream.write(msg_bytes);
                }

                //Otherwise the path/document is not valid and the 403 page will be brought up with the relevant path.
                else {
                    let response: ~str =
                    format!("HTTP/1.1 403 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                    <doctype !html><title>403 Forbidden</title>

 Forbidden

 You don't have permission to access /{:s}

Total Server Refreshes: {}

                    \r\n", tempPath, unsafe{visitCount});
                    stream.write(response.as_bytes());
                };
            }

            //If none of the above two situations were "met", then show the 403 page with the relevant path.
            else {
                let response: ~str =
                format!("HTTP/1.1 403 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                 <doctype !html><title>403 Forbidden</title>

 Forbidden

 You don't have permission to access /{:s}

Total Server Refreshes: {}

                 \r\n", path, unsafe{visitCount});
                stream.write(response.as_bytes());
            };

            //In the command prompt, print out "Connection terminates." as well as the new page visited number.
            println!("Connection terminates.");
            unsafe {
                println!("This page has been called {} time(s).", visitCount);
            }
        }
    }
}<|endoftext|>


<commit_before>use std::old_io::File;
use std::old_io::BufferedReader;
use std::{str,vec,fmt,path};

pub mod scopes;
pub mod ast;
pub mod typeinf;
pub mod nameres;
pub mod codeiter;
pub mod codecleaner;
pub mod testutils;
pub mod util;
pub mod matchers;

#[cfg(test)] pub mod test;
#[cfg(test)] pub mod bench;

#[derive(Debug,Clone,PartialEq)]
pub enum MatchType {
    Struct,
    Module,
    MatchArm,
    Function,
    Crate,
    Let,
    IfLet,
    StructField,
    Impl,
    Enum,
    EnumVariant,
    Type,
    FnArg,
    Trait,
    Const,
    Static
}

impl Copy for MatchType {}

#[derive(Debug)]
pub enum SearchType {
    ExactMatch,
    StartsWith
}

impl Copy for SearchType {}

#[derive(Debug)]
pub enum Namespace {
    TypeNamespace,
    ValueNamespace,
    BothNamespaces
}

impl Copy for Namespace {}

#[derive(Debug)]
pub enum CompletionType {
    CompleteField,
    CompletePath
}

impl Copy for CompletionType {}

#[derive(Clone)]
pub struct Match {
    pub matchstr: String,
    pub filepath: path::Path,
    pub point: usize,
    pub local: bool,
    pub mtype: MatchType,
    pub contextstr: String,
    pub generic_args: Vec<String>,
    pub generic_types: Vec<PathSearch>  // generic types are evaluated lazily
}

impl Match {
    fn with_generic_types(&self, generic_types: Vec<PathSearch>) -> Match {
        Match {
            matchstr: self.matchstr.clone(),
            filepath: self.filepath.clone(),
            point: self.point,
            local: self.local,
            mtype: self.mtype,
            contextstr: self.contextstr.clone(),
            generic_args: self.generic_args.clone(),
            generic_types: generic_types
        }
    }
}

impl fmt::Debug for Match {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Match [{:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?} |{}|]",
               self.matchstr,
               self.filepath.as_str(),
               self.point,
               self.local,
               self.mtype,
               self.generic_args,
               self.generic_types,
               self.contextstr)
    }
}

#[derive(Clone)]
pub struct Scope {
    pub filepath: path::Path,
    pub point: usize
}

impl Scope {
    pub fn from_match(m: &Match) -> Scope {
        Scope{filepath: m.filepath.clone(), point: m.point}
    }
}

impl fmt::Debug for Scope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scope [{:?}, {:?}]",
               self.filepath.as_str(),
               self.point)
    }
}

// Represents a type. Equivilent to rustc's ast::Ty but can be passed across threads
#[derive(Debug,Clone)]
pub enum Ty {
    TyMatch(Match),
    TyPathSearch(Path, Scope),   // A path + the scope to be able to resolve it
    TyTuple(Vec<Ty>),
    TyUnsupported
}

// The racer implementation of an ast::Path. Difference is that it is Send-able
#[derive(Clone)]
pub struct Path {
    global: bool,
    segments: Vec<PathSegment>
}

impl Path {
    pub fn generic_types(&self) -> ::std::slice::Iter<Path> {
        return self.segments[self.segments.len()-1].types.iter();
    }
}

impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "P["));
        let mut first = true;
        for seg in self.segments.iter() {
            if first {
                try!(write!(f, "{}", seg.name));
                first = false;
            } else {
                try!(write!(f, "::{}", seg.name));
            }

            if !seg.types.is_empty() {
                try!(write!(f, "<"));
                let mut tfirst = true;
                for typath in seg.types.iter() {
                    if tfirst {
                        try!(write!(f, "{:?}", typath));
                        tfirst = false;
                    } else {
                        try!(write!(f, ",{:?}", typath))
                    }
                }
                try!(write!(f, ">"));
            }
        }
        return write!(f, "]");
    }
}

#[derive(Debug,Clone)]
pub struct PathSegment {
    pub name: String,
    pub types: Vec<Path>
}

#[derive(Clone)]
pub struct PathSearch {
    path: Path,
    filepath: path::Path,
    point: usize
}

impl fmt::Debug for PathSearch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Search [{:?}, {:?}, {:?}]",
               self.path,
               self.filepath.as_str(),
               self.point)
    }
}

pub fn load_file(filepath: &path::Path) -> String {
    let rawbytes = BufferedReader::new(File::open(filepath)).read_to_end().unwrap();

    // skip BOF bytes, if present
    if rawbytes[0..3] == [0xEF, 0xBB, 0xBF][] {
        let mut it = rawbytes.into_iter();
        it.next(); it.next(); it.next();
        return String::from_utf8(it.collect::<Vec<_>>()).unwrap();
    } else {
        return String::from_utf8(rawbytes).unwrap();
    }
}

pub fn load_file_and_mask_comments(filepath: &path::Path) -> String {
    let filetxt = BufferedReader::new(File::open(filepath)).read_to_end().unwrap();
    let src = str::from_utf8(&filetxt[]).unwrap();
    let msrc = scopes::mask_comments(src);
    return msrc;
}

pub fn complete_from_file(src: &str, filepath: &path::Path, pos: usize) -> vec::IntoIter<Match> {

    let start = scopes::get_start_of_search_expr(src, pos);
    let expr = &src[start..pos];

    let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);

    debug!("{:?}: contextstr is |{}|, searchstr is |{}|",
           completetype, contextstr, searchstr);

    let mut out = Vec::new();

    match completetype {
        CompletionType::CompletePath => {
            let mut v = expr.split_str("::").collect::<Vec<_>>();
            let mut global = false;
            if v[0] == "" {      // i.e. starts with '::' e.g. ::std::old_io::blah
                v.remove(0);
                global = true;
            }

            let segs = v
                .iter()
                .map(|x| PathSegment{name:x.to_string(), types: Vec::new()})
                .collect::<Vec<_>>();
            let path = Path{ global: global, segments: segs };

            for m in nameres::resolve_path(&path, filepath, pos,
                                         SearchType::StartsWith, Namespace::BothNamespaces) {
                out.push(m);
            }
        },
        CompletionType::CompleteField => {
            let context = ast::get_type_of(contextstr.to_string(), filepath, pos);
            debug!("complete_from_file context is {:?}", context);
            context.map(|ty| {
                match ty {
                    Ty::TyMatch(m) => {
                        for m in nameres::search_for_field_or_method(m, searchstr, SearchType::StartsWith) {
                            out.push(m)
                        }
                    }
                    _ => {}
                }
            });
        }
    }
    return out.into_iter();
}

pub fn find_definition(src: &str, filepath: &path::Path, pos: usize) -> Option<Match> {
    return find_definition_(src, filepath, pos);
}

pub fn find_definition_(src: &str, filepath: &path::Path, pos: usize) -> Option<Match> {
    let (start, end) = scopes::expand_search_expr(src, pos);
    let expr = &src[start..end];

    let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);

    debug!("find_definition_ for |{:?}| |{:?}| {:?}",contextstr, searchstr, completetype);

    return match completetype {
        CompletionType::CompletePath => {
            let mut v = expr.split_str("::").collect::<Vec<_>>();
            let mut global = false;
            if v[0] == "" {      // i.e. starts with '::' e.g. ::std::old_io::blah
                v.remove(0);
                global = true;
            }

            let segs = v
                .iter()
                .map(|x| PathSegment{ name: x.to_string(), types: Vec::new() })
                .collect::<Vec<_>>();
            let path = Path{ global: global, segments: segs };

            return nameres::resolve_path(&path, filepath, pos,
                                         SearchType::ExactMatch, Namespace::BothNamespaces).nth(0);
        },
        CompletionType::CompleteField => {
            let context = ast::get_type_of(contextstr.to_string(), filepath, pos);
            debug!("context is {:?}",context);

            return context.and_then(|ty| {
                // for now, just handle matches
                match ty {
                    Ty::TyMatch(m) => {
                        return nameres::search_for_field_or_method(m, searchstr, SearchType::ExactMatch).nth(0);
                    }
                    _ => None
                }
            });
        }
    }
}
<commit_msg>s/path/old_path/<commit_after>use std::old_io::File;
use std::old_io::BufferedReader;
use std::old_path;
use std::{str,vec,fmt};

pub mod scopes;
pub mod ast;
pub mod typeinf;
pub mod nameres;
pub mod codeiter;
pub mod codecleaner;
pub mod testutils;
pub mod util;
pub mod matchers;

#[cfg(test)] pub mod test;
#[cfg(test)] pub mod bench;

#[derive(Debug,Clone,PartialEq)]
pub enum MatchType {
    Struct,
    Module,
    MatchArm,
    Function,
    Crate,
    Let,
    IfLet,
    StructField,
    Impl,
    Enum,
    EnumVariant,
    Type,
    FnArg,
    Trait,
    Const,
    Static
}

impl Copy for MatchType {}

#[derive(Debug)]
pub enum SearchType {
    ExactMatch,
    StartsWith
}

impl Copy for SearchType {}

#[derive(Debug)]
pub enum Namespace {
    TypeNamespace,
    ValueNamespace,
    BothNamespaces
}

impl Copy for Namespace {}

#[derive(Debug)]
pub enum CompletionType {
    CompleteField,
    CompletePath
}

impl Copy for CompletionType {}

#[derive(Clone)]
pub struct Match {
    pub matchstr: String,
    pub filepath: old_path::Path,
    pub point: usize,
    pub local: bool,
    pub mtype: MatchType,
    pub contextstr: String,
    pub generic_args: Vec<String>,
    pub generic_types: Vec<PathSearch>  // generic types are evaluated lazily
}

impl Match {
    fn with_generic_types(&self, generic_types: Vec<PathSearch>) -> Match {
        Match {
            matchstr: self.matchstr.clone(),
            filepath: self.filepath.clone(),
            point: self.point,
            local: self.local,
            mtype: self.mtype,
            contextstr: self.contextstr.clone(),
            generic_args: self.generic_args.clone(),
            generic_types: generic_types
        }
    }
}

impl fmt::Debug for Match {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Match [{:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?} |{}|]",
               self.matchstr,
               self.filepath.as_str(),
               self.point,
               self.local,
               self.mtype,
               self.generic_args,
               self.generic_types,
               self.contextstr)
    }
}

#[derive(Clone)]
pub struct Scope {
    pub filepath: old_path::Path,
    pub point: usize
}

impl Scope {
    pub fn from_match(m: &Match) -> Scope {
        Scope{filepath: m.filepath.clone(), point: m.point}
    }
}

impl fmt::Debug for Scope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scope [{:?}, {:?}]",
               self.filepath.as_str(),
               self.point)
    }
}

// Represents a type. Equivilent to rustc's ast::Ty but can be passed across threads
#[derive(Debug,Clone)]
pub enum Ty {
    TyMatch(Match),
    TyPathSearch(Path, Scope),   // A path + the scope to be able to resolve it
    TyTuple(Vec<Ty>),
    TyUnsupported
}

// The racer implementation of an ast::Path. Difference is that it is Send-able
#[derive(Clone)]
pub struct Path {
    global: bool,
    segments: Vec<PathSegment>
}

impl Path {
    pub fn generic_types(&self) -> ::std::slice::Iter<Path> {
        return self.segments[self.segments.len()-1].types.iter();
    }
}

impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "P["));
        let mut first = true;
        for seg in self.segments.iter() {
            if first {
                try!(write!(f, "{}", seg.name));
                first = false;
            } else {
                try!(write!(f, "::{}", seg.name));
            }

            if !seg.types.is_empty() {
                try!(write!(f, "<"));
                let mut tfirst = true;
                for typath in seg.types.iter() {
                    if tfirst {
                        try!(write!(f, "{:?}", typath));
                        tfirst = false;
                    } else {
                        try!(write!(f, ",{:?}", typath))
                    }
                }
                try!(write!(f, ">"));
            }
        }
        return write!(f, "]");
    }
}

#[derive(Debug,Clone)]
pub struct PathSegment {
    pub name: String,
    pub types: Vec<Path>
}

#[derive(Clone)]
pub struct PathSearch {
    path: Path,
    filepath: old_path::Path,
    point: usize
}

impl fmt::Debug for PathSearch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Search [{:?}, {:?}, {:?}]",
               self.path,
               self.filepath.as_str(),
               self.point)
    }
}

pub fn load_file(filepath: &old_path::Path) -> String {
    let rawbytes = BufferedReader::new(File::open(filepath)).read_to_end().unwrap();

    // skip BOF bytes, if present
    if rawbytes[0..3] == [0xEF, 0xBB, 0xBF][] {
        let mut it = rawbytes.into_iter();
        it.next(); it.next(); it.next();
        return String::from_utf8(it.collect::<Vec<_>>()).unwrap();
    } else {
        return String::from_utf8(rawbytes).unwrap();
    }
}

pub fn load_file_and_mask_comments(filepath: &old_path::Path) -> String {
    let filetxt = BufferedReader::new(File::open(filepath)).read_to_end().unwrap();
    let src = str::from_utf8(&filetxt[]).unwrap();
    let msrc = scopes::mask_comments(src);
    return msrc;
}

pub fn complete_from_file(src: &str, filepath: &old_path::Path, pos: usize) -> vec::IntoIter<Match> {

    let start = scopes::get_start_of_search_expr(src, pos);
    let expr = &src[start..pos];

    let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);

    debug!("{:?}: contextstr is |{}|, searchstr is |{}|",
           completetype, contextstr, searchstr);

    let mut out = Vec::new();

    match completetype {
        CompletionType::CompletePath => {
            let mut v = expr.split_str("::").collect::<Vec<_>>();
            let mut global = false;
            if v[0] == "" {      // i.e. starts with '::' e.g. ::std::old_io::blah
                v.remove(0);
                global = true;
            }

            let segs = v
                .iter()
                .map(|x| PathSegment{name:x.to_string(), types: Vec::new()})
                .collect::<Vec<_>>();
            let path = Path{ global: global, segments: segs };

            for m in nameres::resolve_path(&path, filepath, pos,
                                         SearchType::StartsWith, Namespace::BothNamespaces) {
                out.push(m);
            }
        },
        CompletionType::CompleteField => {
            let context = ast::get_type_of(contextstr.to_string(), filepath, pos);
            debug!("complete_from_file context is {:?}", context);
            context.map(|ty| {
                match ty {
                    Ty::TyMatch(m) => {
                        for m in nameres::search_for_field_or_method(m, searchstr, SearchType::StartsWith) {
                            out.push(m)
                        }
                    }
                    _ => {}
                }
            });
        }
    }
    return out.into_iter();
}

pub fn find_definition(src: &str, filepath: &old_path::Path, pos: usize) -> Option<Match> {
    return find_definition_(src, filepath, pos);
}

pub fn find_definition_(src: &str, filepath: &old_path::Path, pos: usize) -> Option<Match> {
    let (start, end) = scopes::expand_search_expr(src, pos);
    let expr = &src[start..end];

    let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);

    debug!("find_definition_ for |{:?}| |{:?}| {:?}",contextstr, searchstr, completetype);

    return match completetype {
        CompletionType::CompletePath => {
            let mut v = expr.split_str("::").collect::<Vec<_>>();
            let mut global = false;
            if v[0] == "" {      // i.e. starts with '::' e.g. ::std::old_io::blah
                v.remove(0);
                global = true;
            }

            let segs = v
                .iter()
                .map(|x| PathSegment{ name: x.to_string(), types: Vec::new() })
                .collect::<Vec<_>>();
            let path = Path{ global: global, segments: segs };

            return nameres::resolve_path(&path, filepath, pos,
                                         SearchType::ExactMatch, Namespace::BothNamespaces).nth(0);
        },
        CompletionType::CompleteField => {
            let context = ast::get_type_of(contextstr.to_string(), filepath, pos);
            debug!("context is {:?}",context);

            return context.and_then(|ty| {
                // for now, just handle matches
                match ty {
                    Ty::TyMatch(m) => {
                        return nameres::search_for_field_or_method(m, searchstr, SearchType::ExactMatch).nth(0);
                    }
                    _ => None
                }
            });
        }
    }
}
<|endoftext|>


<commit_before>//! Implements a map from integer indices to data.
//! Rather than storing data for every index, internally, this maps entire ranges to the data.
//! To this end, the APIs all work on ranges, not on individual integers. Ranges are split as
//! necessary (e.g. when [0,5) is first associated with X, and then [1,2) is mutated).
//! Users must not depend on whether a range is coalesced or not, even though this is observable
//! via the iteration APIs.
use std::collections::BTreeMap;
use std::ops;

#[derive(Clone, Debug)]
pub struct RangeMap<T> {
    map: BTreeMap<Range, T>,
}

// The derived `Ord` impl sorts first by the first field, then, if the fields are the same,
// by the second field.
// This is exactly what we need for our purposes, since a range query on a BTReeSet/BTreeMap will give us all
// `MemoryRange`s whose `start` is <= than the one we're looking for, but not > the end of the range we're checking.
// At the same time the `end` is irrelevant for the sorting and range searching, but used for the check.
// This kind of search breaks, if `end < start`, so don't do that!
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct Range {
    start: u64,
    end: u64, // Invariant: end > start
}

impl Range {
    fn range(offset: u64, len: u64) -> ops::Range<Range> {
        assert!(len > 0);
        // We select all elements that are within
        // the range given by the offset into the allocation and the length.
        // This is sound if all ranges that intersect with the argument range, are in the
        // resulting range of ranges.
        let left = Range {
            // lowest range to include `offset`
            start: 0,
            end: offset + 1,
        };
        let right = Range {
            // lowest (valid) range not to include `offset+len`
            start: offset + len,
            end: offset + len + 1,
        };
        left..right
    }

    /// Tests if all of [offset, offset+len) are contained in this range.
    fn overlaps(&self, offset: u64, len: u64) -> bool {
        assert!(len > 0);
        offset < self.end && offset + len >= self.start
    }
}

impl<T> RangeMap<T> {
    pub fn new() -> RangeMap<T> {
        RangeMap { map: BTreeMap::new() }
    }

    fn iter_with_range<'a>(
        &'a self,
        offset: u64,
        len: u64,
    ) -> impl Iterator<Item = (&'a Range, &'a T)> + 'a {
        assert!(len > 0);
        self.map.range(Range::range(offset, len)).filter_map(
            move |(range,
                   data)| {
                if range.overlaps(offset, len) {
                    Some((range, data))
                } else {
                    None
                }
            },
        )
    }

    pub fn iter<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item = &'a T> + 'a {
        self.iter_with_range(offset, len).map(|(_, data)| data)
    }

    fn split_entry_at(&mut self, offset: u64)
    where
        T: Clone,
    {
        let range = match self.iter_with_range(offset, 1).next() {
            Some((&range, _)) => range,
            None => return,
        };
        assert!(
            range.start <= offset && range.end > offset,
            "We got a range that doesn't even contain what we asked for."
        );
        // There is an entry overlapping this position, see if we have to split it
        if range.start < offset {
            let data = self.map.remove(&range).unwrap();
            let old = self.map.insert(
                Range {
                    start: range.start,
                    end: offset,
                },
                data.clone(),
            );
            assert!(old.is_none());
            let old = self.map.insert(
                Range {
                    start: offset,
                    end: range.end,
                },
                data,
            );
            assert!(old.is_none());
        }
    }

    pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator<Item = &'a mut T> + 'a {
        self.map.values_mut()
    }

    /// Provide mutable iteration over everything in the given range.  As a side-effect,
    /// this will split entries in the map that are only partially hit by the given range,
    /// to make sure that when they are mutated, the effect is constrained to the given range.
    pub fn iter_mut_with_gaps<'a>(
        &'a mut self,
        offset: u64,
        len: u64,
    ) -> impl Iterator<Item = &'a mut T> + 'a
    where
        T: Clone,
    {
        assert!(len > 0);
        // Preparation: Split first and last entry as needed.
        self.split_entry_at(offset);
        self.split_entry_at(offset + len);
        // Now we can provide a mutable iterator
        self.map.range_mut(Range::range(offset, len)).filter_map(
            move |(&range, data)| {
                if range.overlaps(offset, len) {
                    assert!(
                        offset <= range.start && offset + len >= range.end,
                        "The splitting went wrong"
                    );
                    Some(data)
                } else {
                    // Skip this one
                    None
                }
            },
        )
    }

    /// Provide a mutable iterator over everything in the given range, with the same side-effects as
    /// iter_mut_with_gaps.  Furthermore, if there are gaps between ranges, fill them with the given default.
    /// This is also how you insert.
    pub fn iter_mut<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item = &'a mut T> + 'a
    where
        T: Clone + Default,
    {
        // Do a first iteration to collect the gaps
        let mut gaps = Vec::new();
        let mut last_end = offset;
        for (range, _) in self.iter_with_range(offset, len) {
            if last_end < range.start {
                gaps.push(Range {
                    start: last_end,
                    end: range.start,
                });
            }
            last_end = range.end;
        }
        if last_end < offset + len {
            gaps.push(Range {
                start: last_end,
                end: offset + len,
            });
        }

        // Add default for all gaps
        for gap in gaps {
            let old = self.map.insert(gap, Default::default());
            assert!(old.is_none());
        }

        // Now provide mutable iteration
        self.iter_mut_with_gaps(offset, len)
    }

    pub fn retain<F>(&mut self, mut f: F)
    where
        F: FnMut(&T) -> bool,
    {
        let mut remove = Vec::new();
        for (range, data) in self.map.iter() {
            if !f(data) {
                remove.push(*range);
            }
        }

        for range in remove {
            self.map.remove(&range);
        }
    }
}

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

    /// Query the map at every offset in the range and collect the results.
    fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
        (offset..offset + len)
            .into_iter()
            .map(|i| *map.iter(i, 1).next().unwrap())
            .collect()
    }

    #[test]
    fn basic_insert() {
        let mut map = RangeMap::<i32>::new();
        // Insert
        for x in map.iter_mut(10, 1) {
            *x = 42;
        }
        // Check
        assert_eq!(to_vec(&map, 10, 1), vec![42]);
    }

    #[test]
    fn gaps() {
        let mut map = RangeMap::<i32>::new();
        for x in map.iter_mut(11, 1) {
            *x = 42;
        }
        for x in map.iter_mut(15, 1) {
            *x = 42;
        }

        // Now request a range that needs three gaps filled
        for x in map.iter_mut(10, 10) {
            if *x != 42 {
                *x = 23;
            }
        }

        assert_eq!(
            to_vec(&map, 10, 10),
            vec![23, 42, 23, 23, 23, 42, 23, 23, 23, 23]
        );
        assert_eq!(to_vec(&map, 13, 5), vec![23, 23, 42, 23, 23]);
    }
}
<commit_msg>Workaround for rustc bug<commit_after>//! Implements a map from integer indices to data.
//! Rather than storing data for every index, internally, this maps entire ranges to the data.
//! To this end, the APIs all work on ranges, not on individual integers. Ranges are split as
//! necessary (e.g. when [0,5) is first associated with X, and then [1,2) is mutated).
//! Users must not depend on whether a range is coalesced or not, even though this is observable
//! via the iteration APIs.
use std::collections::BTreeMap;
use std::ops;

#[derive(Clone, Debug)]
pub struct RangeMap<T> {
    map: BTreeMap<Range, T>,
}

// The derived `Ord` impl sorts first by the first field, then, if the fields are the same,
// by the second field.
// This is exactly what we need for our purposes, since a range query on a BTReeSet/BTreeMap will give us all
// `MemoryRange`s whose `start` is <= than the one we're looking for, but not > the end of the range we're checking.
// At the same time the `end` is irrelevant for the sorting and range searching, but used for the check.
// This kind of search breaks, if `end < start`, so don't do that!
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Range {
    start: u64,
    end: u64, // Invariant: end > start
}

impl Range {
    fn range(offset: u64, len: u64) -> ops::Range<Range> {
        assert!(len > 0);
        // We select all elements that are within
        // the range given by the offset into the allocation and the length.
        // This is sound if all ranges that intersect with the argument range, are in the
        // resulting range of ranges.
        let left = Range {
            // lowest range to include `offset`
            start: 0,
            end: offset + 1,
        };
        let right = Range {
            // lowest (valid) range not to include `offset+len`
            start: offset + len,
            end: offset + len + 1,
        };
        left..right
    }

    /// Tests if all of [offset, offset+len) are contained in this range.
    fn overlaps(&self, offset: u64, len: u64) -> bool {
        assert!(len > 0);
        offset < self.end && offset + len >= self.start
    }
}

impl<T> RangeMap<T> {
    pub fn new() -> RangeMap<T> {
        RangeMap { map: BTreeMap::new() }
    }

    fn iter_with_range<'a>(
        &'a self,
        offset: u64,
        len: u64,
    ) -> impl Iterator<Item = (&'a Range, &'a T)> + 'a {
        assert!(len > 0);
        self.map.range(Range::range(offset, len)).filter_map(
            move |(range,
                   data)| {
                if range.overlaps(offset, len) {
                    Some((range, data))
                } else {
                    None
                }
            },
        )
    }

    pub fn iter<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item = &'a T> + 'a {
        self.iter_with_range(offset, len).map(|(_, data)| data)
    }

    fn split_entry_at(&mut self, offset: u64)
    where
        T: Clone,
    {
        let range = match self.iter_with_range(offset, 1).next() {
            Some((&range, _)) => range,
            None => return,
        };
        assert!(
            range.start <= offset && range.end > offset,
            "We got a range that doesn't even contain what we asked for."
        );
        // There is an entry overlapping this position, see if we have to split it
        if range.start < offset {
            let data = self.map.remove(&range).unwrap();
            let old = self.map.insert(
                Range {
                    start: range.start,
                    end: offset,
                },
                data.clone(),
            );
            assert!(old.is_none());
            let old = self.map.insert(
                Range {
                    start: offset,
                    end: range.end,
                },
                data,
            );
            assert!(old.is_none());
        }
    }

    pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator<Item = &'a mut T> + 'a {
        self.map.values_mut()
    }

    /// Provide mutable iteration over everything in the given range.  As a side-effect,
    /// this will split entries in the map that are only partially hit by the given range,
    /// to make sure that when they are mutated, the effect is constrained to the given range.
    pub fn iter_mut_with_gaps<'a>(
        &'a mut self,
        offset: u64,
        len: u64,
    ) -> impl Iterator<Item = &'a mut T> + 'a
    where
        T: Clone,
    {
        assert!(len > 0);
        // Preparation: Split first and last entry as needed.
        self.split_entry_at(offset);
        self.split_entry_at(offset + len);
        // Now we can provide a mutable iterator
        self.map.range_mut(Range::range(offset, len)).filter_map(
            move |(&range, data)| {
                if range.overlaps(offset, len) {
                    assert!(
                        offset <= range.start && offset + len >= range.end,
                        "The splitting went wrong"
                    );
                    Some(data)
                } else {
                    // Skip this one
                    None
                }
            },
        )
    }

    /// Provide a mutable iterator over everything in the given range, with the same side-effects as
    /// iter_mut_with_gaps.  Furthermore, if there are gaps between ranges, fill them with the given default.
    /// This is also how you insert.
    pub fn iter_mut<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item = &'a mut T> + 'a
    where
        T: Clone + Default,
    {
        // Do a first iteration to collect the gaps
        let mut gaps = Vec::new();
        let mut last_end = offset;
        for (range, _) in self.iter_with_range(offset, len) {
            if last_end < range.start {
                gaps.push(Range {
                    start: last_end,
                    end: range.start,
                });
            }
            last_end = range.end;
        }
        if last_end < offset + len {
            gaps.push(Range {
                start: last_end,
                end: offset + len,
            });
        }

        // Add default for all gaps
        for gap in gaps {
            let old = self.map.insert(gap, Default::default());
            assert!(old.is_none());
        }

        // Now provide mutable iteration
        self.iter_mut_with_gaps(offset, len)
    }

    pub fn retain<F>(&mut self, mut f: F)
    where
        F: FnMut(&T) -> bool,
    {
        let mut remove = Vec::new();
        for (range, data) in self.map.iter() {
            if !f(data) {
                remove.push(*range);
            }
        }

        for range in remove {
            self.map.remove(&range);
        }
    }
}

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

    /// Query the map at every offset in the range and collect the results.
    fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
        (offset..offset + len)
            .into_iter()
            .map(|i| *map.iter(i, 1).next().unwrap())
            .collect()
    }

    #[test]
    fn basic_insert() {
        let mut map = RangeMap::<i32>::new();
        // Insert
        for x in map.iter_mut(10, 1) {
            *x = 42;
        }
        // Check
        assert_eq!(to_vec(&map, 10, 1), vec![42]);
    }

    #[test]
    fn gaps() {
        let mut map = RangeMap::<i32>::new();
        for x in map.iter_mut(11, 1) {
            *x = 42;
        }
        for x in map.iter_mut(15, 1) {
            *x = 42;
        }

        // Now request a range that needs three gaps filled
        for x in map.iter_mut(10, 10) {
            if *x != 42 {
                *x = 23;
            }
        }

        assert_eq!(
            to_vec(&map, 10, 10),
            vec![23, 42, 23, 23, 23, 42, 23, 23, 23, 23]
        );
        assert_eq!(to_vec(&map, 13, 5), vec![23, 23, 42, 23, 23]);
    }
}
<|endoftext|>


<commit_before>//! Hooks into the wlroots logging functionality. Internally many events are logged
//! and are reported on standard error. The verbosity of the logs is determined by
//! [the verbosity level](type.LogVerbosity.html) when
//! [initializing the logs](fn.init_logging.html).
//!
//! To log using this system please utilize the [`wlr_log!`](../../macro.wlr_log.html) macro.

use libc::c_char;
use wlroots_sys::{wlr_log_importance, __va_list_tag, wlr_log_init};
use vsprintf::vsprintf;

use utils::c_to_rust_string;

// Export these so it can be used in `wlr_log!`.
pub use self::wlr_log_importance::{WLR_SILENT, WLR_ERROR, WLR_INFO,
                                   WLR_DEBUG, WLR_LOG_IMPORTANCE_LAST};

/// How verbose you want the logging. Lower levels prints more.
pub type LogVerbosity = wlr_log_importance;

/// The signature for the callback function you can hook into the logging
/// functionality of wlroots.
pub type LogCallback = fn(LogVerbosity, String);

static mut RUST_LOGGING_FN: LogCallback = dummy_callback;

/// Initialize wlroots logging at a certain level of verbosity with
/// an optional callback that will be called for every log.
///
/// To log using this system, use the
/// [`wlr_log!`](../../macro.wlr_log.html) macro.
pub fn init_logging<F>(verbosity: LogVerbosity, callback: F)
where F: Into<Option<LogCallback>>
{
    unsafe {
        match callback.into() {
            None => wlr_log_init(verbosity, None),
            Some(callback) => {
                RUST_LOGGING_FN = callback;
                wlr_log_init(verbosity, Some(log_callback));
            }
        }
    }
}

/// Dummy callback to fill in RUST_LOGGING_FN when it's not in use.
fn dummy_callback(_: LogVerbosity, _: String) {}

/// Real hook into the logging callback, calls the real user-supplied callback
/// with nice Rust inputs.
unsafe extern "C" fn log_callback(importance: wlr_log_importance,
                                  fmt: *const c_char,
                                  va_list: *mut __va_list_tag) {
    let message = vsprintf(fmt, va_list).unwrap_or_else(|_| {
        c_to_rust_string(fmt).unwrap_or_else(|| "".into())
    });
    RUST_LOGGING_FN(importance, message);
}
<commit_msg>Add names to the LogCallback params for docs<commit_after>//! Hooks into the wlroots logging functionality. Internally many events are logged
//! and are reported on standard error. The verbosity of the logs is determined by
//! [the verbosity level](type.LogVerbosity.html) when
//! [initializing the logs](fn.init_logging.html).
//!
//! To log using this system please utilize the [`wlr_log!`](../../macro.wlr_log.html) macro.

use libc::c_char;
use wlroots_sys::{wlr_log_importance, __va_list_tag, wlr_log_init};
use vsprintf::vsprintf;

use utils::c_to_rust_string;

// Export these so it can be used in `wlr_log!`.
pub use self::wlr_log_importance::{WLR_SILENT, WLR_ERROR, WLR_INFO,
                                   WLR_DEBUG, WLR_LOG_IMPORTANCE_LAST};

/// How verbose you want the logging. Lower levels prints more.
pub type LogVerbosity = wlr_log_importance;

/// The signature for the callback function you can hook into the logging
/// functionality of wlroots.
///
/// `message` is the formatted string ready to be displayed on the screen.
pub type LogCallback = fn(verbosity: LogVerbosity, message: String);

static mut RUST_LOGGING_FN: LogCallback = dummy_callback;

/// Initialize wlroots logging at a certain level of verbosity with
/// an optional callback that will be called for every log.
///
/// To log using this system, use the
/// [`wlr_log!`](../../macro.wlr_log.html) macro.
pub fn init_logging<F>(verbosity: LogVerbosity, callback: F)
where F: Into<Option<LogCallback>>
{
    unsafe {
        match callback.into() {
            None => wlr_log_init(verbosity, None),
            Some(callback) => {
                RUST_LOGGING_FN = callback;
                wlr_log_init(verbosity, Some(log_callback));
            }
        }
    }
}

/// Dummy callback to fill in RUST_LOGGING_FN when it's not in use.
fn dummy_callback(_: LogVerbosity, _: String) {}

/// Real hook into the logging callback, calls the real user-supplied callback
/// with nice Rust inputs.
unsafe extern "C" fn log_callback(importance: wlr_log_importance,
                                  fmt: *const c_char,
                                  va_list: *mut __va_list_tag) {
    let message = vsprintf(fmt, va_list).unwrap_or_else(|_| {
        c_to_rust_string(fmt).unwrap_or_else(|| "".into())
    });
    RUST_LOGGING_FN(importance, message);
}
<|endoftext|>


<commit_before><commit_msg>chore(compute): small refactoring in a private module<commit_after><|endoftext|>


<commit_before>use sass::output_style::SassOutputStyle;
use ast::node::Node;
use token::{Lexeme, Token};
use error::Result;
use context::Context;

use std::fmt;
use std::io::Write;

#[derive(Clone, PartialEq)]
pub struct SassRule {
    pub selectors: Vec<Lexeme>,
    pub children: Vec<Node>,
}

impl SassRule {
    pub fn new() -> SassRule {
        SassRule {
            selectors: Vec::new(),
            children: Vec::new(),
        }
    }

    fn selector_distribution(&self, parents: &str, separator: &str) -> String {
        match parents.len() {
            0 => self.selectors.iter().map(|s| s.token.to_string()).collect::<Vec<_>>().join(separator),
            _ => parents.split(",").map(|p| {
                self.selectors.iter().map(|s| {
                    if s.token.to_string().contains("&") {
                        s.token.to_string().replace("&", p.trim())
                    } else {
                        format!("{} {}", p.trim(), s.token)
                    }
                }).collect::<Vec<_>>().join(separator)
            }).collect::<Vec<_>>().join(separator),
        }
    }

    pub fn stream<W: Write>(&self, output: &mut W, style: SassOutputStyle) -> Result<()> {
        try!(self.recursive_stream(output, style, "", ""));
        Ok(try!(write!(output, "{}", style.rule_separator())))
    }

    pub fn recursive_stream<W: Write>(&self, output: &mut W, style: SassOutputStyle, parents: &str, nesting: &str) -> Result<()> {
        let mut selector_string = self.selector_distribution(parents, &style.selector_separator());
        if style == SassOutputStyle::Compressed {
            selector_string = compress_selectors(selector_string);
        }

        let mut properties = self.child_properties().into_iter();
        // let mut has_properties = false;

        // TODO: peek?
        if let Some(prop) = properties.next() {
            // has_properties = true;
            try!(write!(output, "{}{}{{{}{}",
              selector_string,
              style.selector_brace_separator(),
              style.brace_property_separator(),
              style.before_property(nesting),
            ));

            try!(prop.stream(output, style));

            for prop in properties {
                try!(write!(output, "{}{}",
                    style.after_property(),
                    style.before_property(nesting),
                ));
                try!(prop.stream(output, style));
            }
            try!(write!(output, "{}}}", style.property_brace_separator()));
        }

        Ok(())
    }

    // TODO: I don't like how child_properties and child_rules are so different, but
    // i'm not sure what to do about it yet.
    pub fn child_properties(&self) -> Vec<&Node> {
        self.children.iter().filter(|c|
            match **c {
                Node::Rule(..) => false,
                Node::Property(..) => true,
            }
        ).collect::<Vec<_>>()
    }

    pub fn child_rules(&self) -> Vec<SassRule> {
        self.children.clone().into_iter().filter_map(|c|
            match c {
                Node::Rule(rule) => Some(rule),
                Node::Property(..) => None,
            }
        ).collect::<Vec<_>>()
    }

    pub fn has_properties(&self) -> bool {
        !self.child_properties().is_empty()
    }

    pub fn optimize(self) -> Vec<SassRule> {
        let mut results = vec![];
        if self.has_properties() {
            results.push(self.clone());
        }
        for cr in self.child_rules().into_iter() {
            results.extend(cr.collapse_with_parent_selectors(&self.selectors));
        }
        results
    }

    pub fn collapse_with_parent_selectors(self, parents: &Vec<Lexeme>) -> Vec<SassRule> {
        let new_selectors = parents.iter().flat_map(|p|
            self.selectors.iter().map(|c|
                Lexeme {
                    token: Token::String(format!("{} {}", p.token, c.token)),
                    offset: p.offset,
                }
            ).collect::<Vec<_>>()
        ).collect();
        SassRule {
            selectors: new_selectors,
            children: self.children,
        }.optimize()
    }

    pub fn evaluate(self, context: &Context) -> SassRule {
        let local_context = context.clone();
        SassRule {
            selectors: self.selectors,
            children: self.children.into_iter().map(|c|
                match c {
                    Node::Rule(sr) => Node::Rule(sr.evaluate(&local_context)),
                    Node::Property(lex, ex) => Node::Property(lex, ex.evaluate(&local_context)),
                }
            ).collect(),
        }
    }
}

fn compress_selectors(selector_string: String) -> String {
    selector_string.replace(" > ", ">").replace(" + ", "+")
}

impl fmt::Debug for SassRule {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().join("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().join("\n  ");
        write!(f, "\n{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::node::Node;
    use ast::expression::Expression;
    use token::{Token, Lexeme};

    #[test]
    fn it_collapses_subrules_without_properties() {
        let innermost_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("strong".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                    ),
                ),
            ],
        };

        let further_in_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("img".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                    ),
                ),
                Node::Rule(innermost_rule),
            ],
        };

        let middle_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("span".into()), offset: Some(0) }],
            children: vec![Node::Rule(further_in_rule)],
        };

        let outer_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
            children: vec![Node::Rule(middle_rule)],
        };

        assert_eq!(
            outer_rule.optimize(),
            vec![
                SassRule {
                    selectors: vec![
                        Lexeme { token: Token::String("div span img".into()), offset: Some(0) },
                    ],
                    children: vec![Node::Property(
                        Lexeme { token: Token::String("color".into()), offset: Some(12) },
                        Expression::String(
                            Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                        ),
                    )],
                },
                SassRule {
                    selectors: vec![
                        Lexeme {
                            token: Token::String("div span img strong".into()),
                            offset: Some(0)
                        },
                    ],
                    children: vec![Node::Property(
                        Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                        Expression::String(
                            Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                        ),
                    )],
                }
            ]
        );
    }
}
<commit_msg>Adjust what I want<commit_after>use sass::output_style::SassOutputStyle;
use ast::node::Node;
use token::{Lexeme, Token};
use error::Result;
use context::Context;

use std::fmt;
use std::io::Write;

#[derive(Clone, PartialEq)]
pub struct SassRule {
    pub selectors: Vec<Lexeme>,
    pub children: Vec<Node>,
}

impl SassRule {
    pub fn new() -> SassRule {
        SassRule {
            selectors: Vec::new(),
            children: Vec::new(),
        }
    }

    fn selector_distribution(&self, parents: &str, separator: &str) -> String {
        match parents.len() {
            0 => self.selectors.iter().map(|s| s.token.to_string()).collect::<Vec<_>>().join(separator),
            _ => parents.split(",").map(|p| {
                self.selectors.iter().map(|s| {
                    if s.token.to_string().contains("&") {
                        s.token.to_string().replace("&", p.trim())
                    } else {
                        format!("{} {}", p.trim(), s.token)
                    }
                }).collect::<Vec<_>>().join(separator)
            }).collect::<Vec<_>>().join(separator),
        }
    }

    pub fn stream<W: Write>(&self, output: &mut W, style: SassOutputStyle) -> Result<()> {
        try!(self.recursive_stream(output, style, "", ""));
        Ok(try!(write!(output, "{}", style.rule_separator())))
    }

    pub fn recursive_stream<W: Write>(&self, output: &mut W, style: SassOutputStyle, parents: &str, nesting: &str) -> Result<()> {
        let mut selector_string = self.selector_distribution(parents, &style.selector_separator());
        if style == SassOutputStyle::Compressed {
            selector_string = compress_selectors(selector_string);
        }

        let mut properties = self.child_properties().into_iter();
        // let mut has_properties = false;

        // TODO: peek?
        if let Some(prop) = properties.next() {
            // has_properties = true;
            try!(write!(output, "{}{}{{{}{}",
              selector_string,
              style.selector_brace_separator(),
              style.brace_property_separator(),
              style.before_property(nesting),
            ));

            try!(prop.stream(output, style));

            for prop in properties {
                try!(write!(output, "{}{}",
                    style.after_property(),
                    style.before_property(nesting),
                ));
                try!(prop.stream(output, style));
            }
            try!(write!(output, "{}}}", style.property_brace_separator()));
        }

        Ok(())
    }

    // TODO: I don't like how child_properties and child_rules are so different, but
    // i'm not sure what to do about it yet.
    pub fn child_properties(&self) -> Vec<&Node> {
        self.children.iter().filter(|c|
            match **c {
                Node::Rule(..) => false,
                Node::Property(..) => true,
            }
        ).collect::<Vec<_>>()
    }

    pub fn child_rules(&self) -> Vec<SassRule> {
        self.children.clone().into_iter().filter_map(|c|
            match c {
                Node::Rule(rule) => Some(rule),
                Node::Property(..) => None,
            }
        ).collect::<Vec<_>>()
    }

    pub fn has_properties(&self) -> bool {
        !self.child_properties().is_empty()
    }

    pub fn optimize(self) -> Vec<SassRule> {
        let mut results = vec![];
        if self.has_properties() {
            results.push(self.clone());
        }
        for cr in self.child_rules().into_iter() {
            results.extend(cr.collapse_with_parent_selectors(&self.selectors));
        }
        results
    }

    pub fn collapse_with_parent_selectors(self, parents: &Vec<Lexeme>) -> Vec<SassRule> {
        let new_selectors = parents.iter().flat_map(|p|
            self.selectors.iter().map(|c|
                Lexeme {
                    token: Token::String(format!("{} {}", p.token, c.token)),
                    offset: p.offset,
                }
            ).collect::<Vec<_>>()
        ).collect();
        SassRule {
            selectors: new_selectors,
            children: self.children,
        }.optimize()
    }

    pub fn evaluate(self, context: &Context) -> SassRule {
        let local_context = context.clone();
        SassRule {
            selectors: self.selectors,
            children: self.children.into_iter().map(|c|
                match c {
                    Node::Rule(sr) => Node::Rule(sr.evaluate(&local_context)),
                    Node::Property(lex, ex) => Node::Property(lex, ex.evaluate(&local_context)),
                }
            ).collect(),
        }
    }
}

fn compress_selectors(selector_string: String) -> String {
    selector_string.replace(" > ", ">").replace(" + ", "+")
}

impl fmt::Debug for SassRule {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let children = self.children.iter().map(|c| format!("{:?}", c)).collect::<Vec<_>>().join("\n");
        let indented_children = children.split("\n").collect::<Vec<_>>().join("\n  ");
        write!(f, "\n{:?} {{\n  {}\n}}", self.selectors, indented_children)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::node::Node;
    use ast::expression::Expression;
    use token::{Token, Lexeme};

    #[test]
    fn it_collapses_subrules_without_properties() {
        let innermost_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("strong".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                    ),
                ),
            ],
        };

        let further_in_rule = SassRule {
            selectors: vec![Lexeme {
                token: Token::String("img".into()),
                offset: Some(6)
            }],
            children: vec![
                Node::Property(
                    Lexeme { token: Token::String("color".into()), offset: Some(12) },
                    Expression::String(
                        Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                    ),
                ),
                Node::Rule(innermost_rule),
            ],
        };

        let middle_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("span".into()), offset: Some(0) }],
            children: vec![Node::Rule(further_in_rule)],
        };

        let outer_rule = SassRule {
            selectors: vec![Lexeme { token: Token::String("div".into()), offset: Some(0) }],
            children: vec![Node::Rule(middle_rule)],
        };

        assert_eq!(
            outer_rule.optimize(),
            vec![
                SassRule {
                    selectors: vec![
                        Lexeme { token: Token::String("div span img".into()), offset: Some(0) },
                    ],
                    children: vec![
                        Node::Property(
                            Lexeme { token: Token::String("color".into()), offset: Some(12) },
                            Expression::String(
                                Lexeme { token: Token::String("blue".into()), offset: Some(19) }
                            ),
                        ),
                        Node::Rule(
                            SassRule {
                                selectors: vec![
                                    Lexeme {
                                        token: Token::String("strong".into()),
                                        offset: Some(6)
                                    },
                                ],
                                children: vec![Node::Property(
                                    Lexeme { token: Token::String("font-weight".into()), offset: Some(12) },
                                    Expression::String(
                                        Lexeme { token: Token::String("bold".into()), offset: Some(19) }
                                    ),
                                )],
                            }
                        ),
                    ],
                },
            ]
        );
    }
}
<|endoftext|>