<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Tagged stream
//!
//! A tagged stream recursively applies tags to regions of an input stream. A stream consisting of a single tag forms a tree,
//! so this stream type is useful for representing the parsed represention of an input.
//!
//! The simplest form of tagged stream is one that's just a set of untagged symbols. This can be created using the `TaggedStream::from_reader`
//! method.
//!
//! ```
//! # use concordance::*;
//! #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
//! enum SomeTag {
//!     Identifier
//! };
//!
//! let simple_tagged : TaggedStream<char, SomeTag> = TaggedStream::from_reader(&mut "Hello, world".read_symbols());
//! ```
//!

use std::slice::Iter;
use std::ops::Index;
use std::ops::Range;

use super::symbol_reader::*;

///
/// Represents a symbol in a tagged stream.
///
/// A symbol can either be a sequence of untagged base symbols, or it can be a tag (which itself can contain any number of tagged symbol)
///
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TagSymbol<Base: Clone, Tag: Clone> {
    /// An untagged element in this stream
    Untagged(Base),

    /// A tagged region of the stream
    Tagged(Tag, TaggedStream<Base, Tag>)
}

///
/// Represents a stream of tagged symbols
///
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TaggedStream<Base: Clone, Tag: Clone> {
    /// The data in this stream
    data: Vec<TagSymbol<Base, Tag>>
}

impl<Base: Clone, Tag: Clone> TaggedStream<Base, Tag> {
    ///
    /// Creates a basic tagged stream from a source of the base symbol
    ///
    pub fn from_reader(reader: &mut SymbolReader<Base>) -> TaggedStream<Base, Tag> {
        // Read from the reader into an array
        let mut symbols = vec![];

        while let Some(next_symbol) = reader.next_symbol() {
            symbols.push(TagSymbol::Untagged(next_symbol));
        }

        // Generate a simple tagged stream from the result
        TaggedStream { data: symbols }
    }

    ///
    /// The number of symbols in this stream
    ///
    pub fn len(&self) -> usize {
        self.data.len()
    }

    ///
    /// Replaces a range in this stream with a tag
    ///
    pub fn tag(&mut self, tag: Tag, range: Range<usize>) {
        // Create a tag to replace the range
        let replaced_symbols    = self.data[range.clone()].to_vec();
        let tag_symbol          = TagSymbol::Tagged(tag, TaggedStream { data: replaced_symbols });

        // Draining seems to be for reading a range but does double duty for deleting a range?
        // I don't think rust has a way to replace a range in a vector, or at least not one that's easy to find in the docs.
        self.data.drain(range.clone());

        // Draining then inserting is inefficient compared to flat out replacing items :-/ This may be possible but the vec docs aren't very easy to read
        self.data.insert(range.start, tag_symbol);
    }
}

impl<Base: Clone, Tag: Clone> Index<usize> for TaggedStream<Base, Tag> {
    type Output = TagSymbol<Base, Tag>;

    fn index(&self, index: usize) -> &TagSymbol<Base, Tag> {
        &self.data[index]
    }
}

impl<'a, Base: Clone, Tag: Clone> SymbolSource<'a, TagSymbol<Base, Tag>> for &'a TaggedStream<Base, Tag> {
    type SymbolReader = Iter<'a, TagSymbol<Base, Tag>>;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader {
        self.data.read_symbols()
    }
}

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

    #[test]
    fn can_tag_range() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            Hello,
            World
        }

        let mut tagged: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());

        tagged.tag(Tags::Hello, 0..5);
        tagged.tag(Tags::World, 1..6);

        assert!(tagged.len() == 2);

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[0] {
            assert!(*tag == Tags::Hello);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('H'));
            assert!(stream[1] == TagSymbol::Untagged('e'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('o'));
        } else {
            assert!(false);
        }

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[1] {
            assert!(*tag == Tags::World);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('W'));
            assert!(stream[1] == TagSymbol::Untagged('o'));
            assert!(stream[2] == TagSymbol::Untagged('r'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('d'));
        } else {
            assert!(false);
        }
    }
}
<commit_msg>Add a way to add a bunch of tags all at once to a tagged stream (and generate a new tagged stream as a result)<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Tagged stream
//!
//! A tagged stream recursively applies tags to regions of an input stream. A stream consisting of a single tag forms a tree,
//! so this stream type is useful for representing the parsed represention of an input.
//!
//! The simplest form of tagged stream is one that's just a set of untagged symbols. This can be created using the `TaggedStream::from_reader`
//! method.
//!
//! ```
//! # use concordance::*;
//! #[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
//! enum SomeTag {
//!     Identifier
//! };
//!
//! let simple_tagged : TaggedStream<char, SomeTag> = TaggedStream::from_reader(&mut "Hello, world".read_symbols());
//! ```
//!

use std::slice::Iter;
use std::ops::Index;
use std::ops::Range;

use super::symbol_reader::*;

///
/// Represents a symbol in a tagged stream.
///
/// A symbol can either be a sequence of untagged base symbols, or it can be a tag (which itself can contain any number of tagged symbol)
///
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum TagSymbol<Base: Clone, Tag: Clone> {
    /// An untagged element in this stream
    Untagged(Base),

    /// A tagged region of the stream
    Tagged(Tag, TaggedStream<Base, Tag>)
}

///
/// Represents a stream of tagged symbols
///
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct TaggedStream<Base: Clone, Tag: Clone> {
    /// The data in this stream
    data: Vec<TagSymbol<Base, Tag>>
}

impl<Base: Clone, Tag: Clone> TaggedStream<Base, Tag> {
    ///
    /// Creates a basic tagged stream from a source of the base symbol
    ///
    pub fn from_reader(reader: &mut SymbolReader<Base>) -> TaggedStream<Base, Tag> {
        // Read from the reader into an array
        let mut symbols = vec![];

        while let Some(next_symbol) = reader.next_symbol() {
            symbols.push(TagSymbol::Untagged(next_symbol));
        }

        // Generate a simple tagged stream from the result
        TaggedStream { data: symbols }
    }

    ///
    /// The number of symbols in this stream
    ///
    pub fn len(&self) -> usize {
        self.data.len()
    }

    ///
    /// Replaces a range in this stream with a tag
    ///
    pub fn tag(&mut self, tag: Tag, range: Range<usize>) {
        // Create a tag to replace the range
        let replaced_symbols    = self.data[range.clone()].to_vec();
        let tag_symbol          = TagSymbol::Tagged(tag, TaggedStream { data: replaced_symbols });

        // Draining seems to be for reading a range but does double duty for deleting a range?
        // I don't think rust has a way to replace a range in a vector, or at least not one that's easy to find in the docs.
        self.data.drain(range.clone());

        // Draining then inserting is inefficient compared to flat out replacing items :-/ This may be possible but the vec docs aren't very easy to read
        self.data.insert(range.start, tag_symbol);
    }

    ///
    /// Creates a tag symbol by tagging a particular range within this stream
    ///
    pub fn tag_range(&self, range: Range<usize>, tag: Tag) -> TagSymbol<Base, Tag> {
        let tag_data = self.data[range].to_vec();

        TagSymbol::Tagged(tag, TaggedStream { data: tag_data })
    }

    ///
    /// Applies tags to the specified ranges in this stream
    ///
    /// The tag ranges must be in ascending order and must not overlap
    ///
    pub fn with_tags(&self, tags: &Vec<(Range<usize>, Tag)>) -> TaggedStream<Base, Tag> {
        // The data that will make up the new stream
        let mut new_stream = vec![];

        // The last processed range (we haven't processed any range yet, so this starts at 0..0)
        let mut last_range = 0..0;

        // Build up the result from the tag list and the last range
        for tag in tags {
            // Fetch the information for this tag and check ordering
            let &(ref next_range, ref next_tag) = tag;
            let end                     = last_range.end;

            if end > next_range.start {
                panic!("Tags for with_tags must be sorted in order");
            }

            // Sometimes need to leave some data from this stream untagged
            if end < next_range.start {
                new_stream.extend(self.data[end..next_range.start].iter().cloned());
            }

            // Push the tag
            new_stream.push(self.tag_range(next_range.clone(), next_tag.clone()));

            // Update state
            last_range = next_range.clone();
        }

        // Append anything left in the stream
        if last_range.end < self.data.len() {
            new_stream.extend(self.data[last_range.end..self.data.len()].iter().cloned());
        }

        // Final result
        TaggedStream { data: new_stream }
    }
}

impl<Base: Clone, Tag: Clone> Index<usize> for TaggedStream<Base, Tag> {
    type Output = TagSymbol<Base, Tag>;

    fn index(&self, index: usize) -> &TagSymbol<Base, Tag> {
        &self.data[index]
    }
}

impl<'a, Base: Clone, Tag: Clone> SymbolSource<'a, TagSymbol<Base, Tag>> for &'a TaggedStream<Base, Tag> {
    type SymbolReader = Iter<'a, TagSymbol<Base, Tag>>;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader {
        self.data.read_symbols()
    }
}

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

    #[test]
    fn can_tag_range() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            Hello,
            World
        }

        let mut tagged: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());

        tagged.tag(Tags::Hello, 0..5);
        tagged.tag(Tags::World, 1..6);

        assert!(tagged.len() == 2);

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[0] {
            assert!(*tag == Tags::Hello);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('H'));
            assert!(stream[1] == TagSymbol::Untagged('e'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('o'));
        } else {
            assert!(false);
        }

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[1] {
            assert!(*tag == Tags::World);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('W'));
            assert!(stream[1] == TagSymbol::Untagged('o'));
            assert!(stream[2] == TagSymbol::Untagged('r'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('d'));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn can_tag_everything_with_tags() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            Hello,
            World
        }

        let original: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());
        let tagged = original.with_tags(&vec![(0..5, Tags::Hello), (5..10, Tags::World)]);

        assert!(tagged.len() == 2);

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[0] {
            assert!(*tag == Tags::Hello);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('H'));
            assert!(stream[1] == TagSymbol::Untagged('e'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('o'));
        } else {
            assert!(false);
        }

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[1] {
            assert!(*tag == Tags::World);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('W'));
            assert!(stream[1] == TagSymbol::Untagged('o'));
            assert!(stream[2] == TagSymbol::Untagged('r'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('d'));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn with_tags_preserves_middle() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            Hello,
            World
        }

        let original: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());
        let tagged = original.with_tags(&vec![(0..4, Tags::Hello), (6..10, Tags::World)]);

        assert!(tagged.len() == 4);
        assert!(tagged[1] == TagSymbol::Untagged('o'));
        assert!(tagged[2] == TagSymbol::Untagged('W'));

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[0] {
            assert!(*tag == Tags::Hello);
            assert!(stream.len() == 4);
            assert!(stream[0] == TagSymbol::Untagged('H'));
            assert!(stream[1] == TagSymbol::Untagged('e'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
        } else {
            assert!(false);
        }

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[3] {
            assert!(*tag == Tags::World);
            assert!(stream.len() == 4);
            assert!(stream[0] == TagSymbol::Untagged('o'));
            assert!(stream[1] == TagSymbol::Untagged('r'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('d'));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn with_tags_preserves_end() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            Hello
        }

        let original: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());
        let tagged = original.with_tags(&vec![(0..5, Tags::Hello)]);

        assert!(tagged.len() == 6);

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[0] {
            assert!(*tag == Tags::Hello);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('H'));
            assert!(stream[1] == TagSymbol::Untagged('e'));
            assert!(stream[2] == TagSymbol::Untagged('l'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('o'));
        } else {
            assert!(false);
        }

        assert!(tagged[1] == TagSymbol::Untagged('W'));
        assert!(tagged[2] == TagSymbol::Untagged('o'));
        assert!(tagged[3] == TagSymbol::Untagged('r'));
        assert!(tagged[4] == TagSymbol::Untagged('l'));
        assert!(tagged[5] == TagSymbol::Untagged('d'));
    }

    #[test]
    fn with_tags_preserves_start() {
        #[derive(Clone, PartialEq, Eq, Copy)]
        enum Tags {
            World
        }

        let original: TaggedStream<char, Tags> = TaggedStream::from_reader(&mut "HelloWorld".read_symbols());
        let tagged = original.with_tags(&vec![(5..10, Tags::World)]);

        assert!(tagged.len() == 6);

        if let TagSymbol::Tagged(ref tag, ref stream) = tagged[5] {
            assert!(*tag == Tags::World);
            assert!(stream.len() == 5);
            assert!(stream[0] == TagSymbol::Untagged('W'));
            assert!(stream[1] == TagSymbol::Untagged('o'));
            assert!(stream[2] == TagSymbol::Untagged('r'));
            assert!(stream[3] == TagSymbol::Untagged('l'));
            assert!(stream[4] == TagSymbol::Untagged('d'));
        } else {
            assert!(false);
        }

        assert!(tagged[0] == TagSymbol::Untagged('H'));
        assert!(tagged[1] == TagSymbol::Untagged('e'));
        assert!(tagged[2] == TagSymbol::Untagged('l'));
        assert!(tagged[3] == TagSymbol::Untagged('l'));
        assert!(tagged[4] == TagSymbol::Untagged('o'));
    }
}
<|endoftext|>


<commit_before>
extern crate chrono;
extern crate rustc_serialize;

pub mod daterecord;
pub mod direction;
pub mod error;
pub mod timeentry;

use chrono::*;
use csv;
use self::daterecord::DateRecord;
use self::direction::Direction;
use self::error::TimeClockError;
use self::timeentry::TimeEntry;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::SeekFrom;
use std::io::prelude::*;

pub fn read_timesheet<R: Read>(file: R)
                               -> Result<Vec<TimeEntry>, TimeClockError> {
    let mut rdr = csv::Reader::from_reader(file).has_headers(false);
    let mut in_v = rdr.decode().collect::<csv::Result<Vec<TimeEntry>>>()?;
    in_v.sort_by_key(|k| k.time);

    return Ok(in_v);
}

/// Match TimeEntrys into pairs of In, Out records, inserting new ones where
/// complete pairs can't be made.
pub fn pair_time_entries(records: Vec<TimeEntry>) -> Vec<TimeEntry> {
    let mut v: Vec<TimeEntry> = Vec::new();

    let mut dir = Direction::In;
    let mut prev_time: Option<DateTime<FixedOffset>> = None;

    for rec in records {
        match (dir, rec.dir) {
            (Direction::In, Direction::Out) => {
                // expected an In, got an Out
                v.push(TimeEntry {
                    dir: Direction::In,
                    time: rec.time,
                    memo: String::from("Missing clock in."),
                });
                v.push(rec.clone());
                dir = Direction::Out;
            }
            (Direction::Out, Direction::In) => {
                // expected an Out, got an In
                v.push(TimeEntry {
                    dir: Direction::Out,
                    time: prev_time.or(Some(rec.time)).unwrap(),
                    memo: String::from("Missing clock out."),
                });
                v.push(rec.clone());
                dir = Direction::Out;
            }
            (_, _) => v.push(rec.clone()),
        }
        dir = dir.reverse();
        prev_time = Some(rec.time);
    }

    // if the for loop ends on a Direction::In record, then we need to add
    // an out record to balance it.
    if v.len() % 2 != 0 {
        v.push(TimeEntry {
            dir: Direction::Out,
            time: now(),
            memo: String::from("Still clocked in."),
        });
    }

    return v;
}

/// Reduce pairs of TimeEntrys into DateRecords
pub fn collect_date_records(records: Vec<TimeEntry>) -> Vec<DateRecord> {
    let mut date_duration_map = BTreeMap::new();

    for slc in records.chunks(2) {
        let r = DateRecord::from_time_entries(&slc[0], &slc[1]);

        if !date_duration_map.contains_key(&r.date) {
            date_duration_map.insert(r.date, r);
        } else {
            let mut rec = date_duration_map.remove(&r.date).unwrap();
            rec.duration = rec.duration + r.duration;
            if !r.memo.is_empty() {
                if !rec.memo.is_empty() {
                    rec.memo.push_str(",");
                }
                rec.memo.push_str(&r.memo);
            }

            date_duration_map.insert(rec.date, rec);
        }
    }

    let v: Vec<DateRecord> = date_duration_map.iter()
        .map(|(_, r)| r.clone())
        .collect();

    return v;
}

/// Marks the time.
pub fn mark_time(d: Direction,
                 time: DateTime<FixedOffset>,
                 memo: String,
                 mut file: File) {
    let record = TimeEntry {
        dir: d,
        time: time,
        memo: memo,
    };
    // seek in case we write without reading first
    let _ = file.seek(SeekFrom::End(0));
    let mut wtr = csv::Writer::from_writer(file);
    let _ = wtr.encode(record);
}

pub fn now() -> DateTime<FixedOffset> {
    let lt: DateTime<Local> = Local::now();
    DateTime::parse_from_rfc3339(lt.to_rfc3339().as_ref()).unwrap()
}

#[cfg(test)]
mod tests {
    use timeclock::direction::Direction;
    use std::io::Cursor;
    use super::*;

    #[test]
    fn read_timesheet_test() {
        // test for sorting and general function
        let s = "Out,2016-12-18T16:53:33-0600,\n\
                 In,2016-12-19T20:54:53-0600,\n\
                 In,2016-12-18T13:01:50-0600,\n\
                 Out,2016-12-19T20:54:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let records = read_timesheet(buff).unwrap();
        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        // test ordering
        assert!(records[0].time < records[1].time);
        assert!(records[1].time < records[2].time);
        assert!(records[2].time < records[3].time);
    }

    #[test]
    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value")]
    fn read_timesheet_haggis_test() {
        // test for sorting and general function
        let s = "Fair fa' your honest, sonsie face,\n\
                 Great chieftain o' the pudding-race!\n\
                 Aboon them a' ye tak your place,\n\
                 Painch, tripe, or thairm:\n\
                 Weel are ye wordy o' a grace\n\
                 As lang's my arm.";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let records = read_timesheet(buff).unwrap();
        assert!(records[0].dir == Direction::In);
    }

    #[test]
    fn pair_time_entries_test() {
        let s = "Out,2016-12-18T13:01:50-0600,\n\
                 In,2016-12-19T20:54:57-0600,\n\
                 Out,2016-12-20T20:50:57-0600,\n\
                 Out,2016-12-20T20:50:59-0600,\n\
                 In,2016-12-20T21:04:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let raw_records = read_timesheet(buff).unwrap();
        let records = pair_time_entries(raw_records);

        println!("\nlen={}", records.len());
        for record in records.clone() {
            println!("{}", record);
        }

        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        assert!(records[4].dir == Direction::In);
        assert!(records[5].dir == Direction::Out);
        assert!(records[6].dir == Direction::In);
        assert!(records[7].dir == Direction::Out);
        assert!(records.len() == 8);
    }

    #[test]
    fn pair_time_entries_double_in_test() {
        let s = "In,2016-12-20T21:01:57-0600,\n\
                 In,2016-12-20T21:04:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let raw_records = read_timesheet(buff).unwrap();
        let records = pair_time_entries(raw_records);

        println!("\nlen={}", records.len());
        for record in records.clone() {
            println!("{}", record);
        }

        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        assert!(records.len() == 4);
    }

    #[test]
    fn collect_date_records_tz_test() {
        // test mismatched timezones
        let s = "In,2016-12-18T13:01:50-0500,\n\
                 Out,2016-12-18T13:01:50-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let entries = read_timesheet(buff).unwrap();
        let paired_records = pair_time_entries(entries);
        let records = collect_date_records(paired_records);

        assert!(records.len() == 1);
        println!("\n{}", records[0].seconds());
        assert!(records[0].seconds() == 3600.0);
    }
}
<commit_msg>appeased the rustfmt gods<commit_after>
extern crate chrono;
extern crate rustc_serialize;

pub mod daterecord;
pub mod direction;
pub mod error;
pub mod timeentry;

use chrono::*;
use csv;
use self::daterecord::DateRecord;
use self::direction::Direction;
use self::error::TimeClockError;
use self::timeentry::TimeEntry;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::SeekFrom;
use std::io::prelude::*;

pub fn read_timesheet<R: Read>(file: R)
                               -> Result<Vec<TimeEntry>, TimeClockError> {
    let mut rdr = csv::Reader::from_reader(file).has_headers(false);
    let mut in_v = rdr.decode().collect::<csv::Result<Vec<TimeEntry>>>()?;
    in_v.sort_by_key(|k| k.time);

    return Ok(in_v);
}

/// Match TimeEntrys into pairs of In, Out records, inserting new ones where
/// complete pairs can't be made.
pub fn pair_time_entries(records: Vec<TimeEntry>) -> Vec<TimeEntry> {
    let mut v: Vec<TimeEntry> = Vec::new();

    let mut dir = Direction::In;
    let mut prev_time: Option<DateTime<FixedOffset>> = None;

    for rec in records {
        match (dir, rec.dir) {
            (Direction::In, Direction::Out) => {
                // expected an In, got an Out
                v.push(TimeEntry {
                    dir: Direction::In,
                    time: rec.time,
                    memo: String::from("Missing clock in."),
                });
                v.push(rec.clone());
                dir = Direction::Out;
            }
            (Direction::Out, Direction::In) => {
                // expected an Out, got an In
                v.push(TimeEntry {
                    dir: Direction::Out,
                    time: prev_time.or(Some(rec.time)).unwrap(),
                    memo: String::from("Missing clock out."),
                });
                v.push(rec.clone());
                dir = Direction::Out;
            }
            (_, _) => v.push(rec.clone()),
        }
        dir = dir.reverse();
        prev_time = Some(rec.time);
    }

    // if the for loop ends on a Direction::In record, then we need to add
    // an out record to balance it.
    if v.len() % 2 != 0 {
        v.push(TimeEntry {
            dir: Direction::Out,
            time: now(),
            memo: String::from("Still clocked in."),
        });
    }

    return v;
}

/// Reduce pairs of TimeEntrys into DateRecords
pub fn collect_date_records(records: Vec<TimeEntry>) -> Vec<DateRecord> {
    let mut date_duration_map = BTreeMap::new();

    for slc in records.chunks(2) {
        let r = DateRecord::from_time_entries(&slc[0], &slc[1]);

        if !date_duration_map.contains_key(&r.date) {
            date_duration_map.insert(r.date, r);
        } else {
            let mut rec = date_duration_map.remove(&r.date).unwrap();
            rec.duration = rec.duration + r.duration;
            if !r.memo.is_empty() {
                if !rec.memo.is_empty() {
                    rec.memo.push_str(",");
                }
                rec.memo.push_str(&r.memo);
            }

            date_duration_map.insert(rec.date, rec);
        }
    }

    let v: Vec<DateRecord> = date_duration_map.iter()
        .map(|(_, r)| r.clone())
        .collect();

    return v;
}

/// Marks the time.
pub fn mark_time(d: Direction,
                 time: DateTime<FixedOffset>,
                 memo: String,
                 mut file: File) {
    let record = TimeEntry {
        dir: d,
        time: time,
        memo: memo,
    };
    // seek in case we write without reading first
    let _ = file.seek(SeekFrom::End(0));
    let mut wtr = csv::Writer::from_writer(file);
    let _ = wtr.encode(record);
}

pub fn now() -> DateTime<FixedOffset> {
    let lt: DateTime<Local> = Local::now();
    DateTime::parse_from_rfc3339(lt.to_rfc3339().as_ref()).unwrap()
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;
    use super::*;
    use timeclock::direction::Direction;

    #[test]
    fn read_timesheet_test() {
        // test for sorting and general function
        let s = "Out,2016-12-18T16:53:33-0600,\n\
                 In,2016-12-19T20:54:53-0600,\n\
                 In,2016-12-18T13:01:50-0600,\n\
                 Out,2016-12-19T20:54:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let records = read_timesheet(buff).unwrap();
        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        // test ordering
        assert!(records[0].time < records[1].time);
        assert!(records[1].time < records[2].time);
        assert!(records[2].time < records[3].time);
    }

    #[test]
    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value")]
    fn read_timesheet_haggis_test() {
        // test for sorting and general function
        let s = "Fair fa' your honest, sonsie face,\n\
                 Great chieftain o' the pudding-race!\n\
                 Aboon them a' ye tak your place,\n\
                 Painch, tripe, or thairm:\n\
                 Weel are ye wordy o' a grace\n\
                 As lang's my arm.";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let records = read_timesheet(buff).unwrap();
        assert!(records[0].dir == Direction::In);
    }

    #[test]
    fn pair_time_entries_test() {
        let s = "Out,2016-12-18T13:01:50-0600,\n\
                 In,2016-12-19T20:54:57-0600,\n\
                 Out,2016-12-20T20:50:57-0600,\n\
                 Out,2016-12-20T20:50:59-0600,\n\
                 In,2016-12-20T21:04:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let raw_records = read_timesheet(buff).unwrap();
        let records = pair_time_entries(raw_records);

        println!("\nlen={}", records.len());
        for record in records.clone() {
            println!("{}", record);
        }

        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        assert!(records[4].dir == Direction::In);
        assert!(records[5].dir == Direction::Out);
        assert!(records[6].dir == Direction::In);
        assert!(records[7].dir == Direction::Out);
        assert!(records.len() == 8);
    }

    #[test]
    fn pair_time_entries_double_in_test() {
        let s = "In,2016-12-20T21:01:57-0600,\n\
                 In,2016-12-20T21:04:57-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let raw_records = read_timesheet(buff).unwrap();
        let records = pair_time_entries(raw_records);

        println!("\nlen={}", records.len());
        for record in records.clone() {
            println!("{}", record);
        }

        assert!(records[0].dir == Direction::In);
        assert!(records[1].dir == Direction::Out);
        assert!(records[2].dir == Direction::In);
        assert!(records[3].dir == Direction::Out);
        assert!(records.len() == 4);
    }

    #[test]
    fn collect_date_records_tz_test() {
        // test mismatched timezones
        let s = "In,2016-12-18T13:01:50-0500,\n\
                 Out,2016-12-18T13:01:50-0600,";
        let vs = s.as_bytes();
        let buff = Cursor::new(vs);
        let entries = read_timesheet(buff).unwrap();
        let paired_records = pair_time_entries(entries);
        let records = collect_date_records(paired_records);

        assert!(records.len() == 1);
        println!("\n{}", records[0].seconds());
        assert!(records[0].seconds() == 3600.0);
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::{Error, UserError};
use interpreter::env::{self, env_module};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};

/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance<E: UserError> {
	/// Shared data reference.
	essence: Arc<ProgramInstanceEssence<E>>,
}

/// Program instance essence.
pub struct ProgramInstanceEssence<E: UserError> {
	/// Loaded modules.
	modules: RwLock<HashMap<String, Arc<ModuleInstanceInterface<E>>>>,
}

impl<E> ProgramInstance<E> where E: UserError {
	/// Create new program instance.
	pub fn new() -> Result<Self, Error<E>> {
		ProgramInstance::with_env_params(env::EnvParams::default())
	}

	/// Create new program instance with custom env module params (mostly memory)
	pub fn with_env_params(params: env::EnvParams) -> Result<Self, Error<E>> {
		Ok(ProgramInstance {
			essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?),
		})
	}

	/// Instantiate module with validation.
	pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface<E> + 'a>>>) -> Result<Arc<ModuleInstance<E>>, Error<E>> {
		let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
		module_instance.instantiate(externals)?;

		let module_instance = Arc::new(module_instance);
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		module_instance.run_start_function()?;
		Ok(module_instance)
	}

	/// Insert instantiated module.
	pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstance<E>>) -> Result<Arc<ModuleInstance<E>>, Error<E>> {
		// replace existing module with the same name with new one
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		Ok(module_instance)
	}

	/// Get one of the modules by name
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface<E>>> {
		self.essence.module(name)
	}
}

impl<E> ProgramInstanceEssence<E> where E: UserError {
	/// Create new program essence.
	pub fn new() -> Result<Self, Error<E>> {
		ProgramInstanceEssence::with_env_params(env::EnvParams::default())
	}

	pub fn with_env_params(env_params: env::EnvParams) -> Result<Self, Error<E>> {
		let mut modules = HashMap::new();
		let env_module: Arc<ModuleInstanceInterface<E>> = Arc::new(env_module(env_params)?);
		modules.insert("env".into(), env_module);
		Ok(ProgramInstanceEssence {
			modules: RwLock::new(modules),
		})
	}

	/// Get module reference.
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface<E>>> {
		self.modules.read().get(name).cloned()
	}
}
<commit_msg>interpreter: ProgramInstance exposes constructor for arbitrary env module<commit_after>use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::{Error, UserError};
use interpreter::env::{self, env_module};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};

/// Program instance. Program is a set of instantiated modules.
pub struct ProgramInstance<E: UserError> {
	/// Shared data reference.
	essence: Arc<ProgramInstanceEssence<E>>,
}

/// Program instance essence.
pub struct ProgramInstanceEssence<E: UserError> {
	/// Loaded modules.
	modules: RwLock<HashMap<String, Arc<ModuleInstanceInterface<E>>>>,
}

impl<E> ProgramInstance<E> where E: UserError {
	/// Create new program instance.
	pub fn new() -> Result<Self, Error<E>> {
		ProgramInstance::with_env_params(env::EnvParams::default())
	}

	/// Create new program instance with custom env module params (mostly memory)
	pub fn with_env_params(params: env::EnvParams) -> Result<Self, Error<E>> {
		Ok(ProgramInstance {
			essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?),
		})
	}

	/// Create a new program instance with a custom env module
	pub fn with_env_module(env_module: Arc<ModuleInstanceInterface<E>>) -> Self {
		ProgramInstance {
			essence: Arc::new(ProgramInstanceEssence::with_env_module(env_module)),
		}
	}

	/// Instantiate module with validation.
	pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface<E> + 'a>>>) -> Result<Arc<ModuleInstance<E>>, Error<E>> {
		let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?;
		module_instance.instantiate(externals)?;

		let module_instance = Arc::new(module_instance);
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		module_instance.run_start_function()?;
		Ok(module_instance)
	}

	/// Insert instantiated module.
	pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstance<E>>) -> Result<Arc<ModuleInstance<E>>, Error<E>> {
		// replace existing module with the same name with new one
		self.essence.modules.write().insert(name.into(), module_instance.clone());
		Ok(module_instance)
	}

	/// Get one of the modules by name
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface<E>>> {
		self.essence.module(name)
	}
}

impl<E> ProgramInstanceEssence<E> where E: UserError {
	/// Create new program essence.
	pub fn new() -> Result<Self, Error<E>> {
		ProgramInstanceEssence::with_env_params(env::EnvParams::default())
	}

	pub fn with_env_params(env_params: env::EnvParams) -> Result<Self, Error<E>> {
		let env_mod = env_module(env_params)?;
		Ok(ProgramInstanceEssence::with_env_module(Arc::new(env_mod)))
	}

	pub fn with_env_module(env_module: Arc<ModuleInstanceInterface<E>>) -> Self {
		let mut modules = HashMap::new();
		modules.insert("env".into(), env_module);
		ProgramInstanceEssence {
			modules: RwLock::new(modules),
		}
	}

	/// Get module reference.
	pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface<E>>> {
		self.modules.read().get(name).cloned()
	}
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{Result, Write};

use super::nodes::*;

pub struct Printer<O: Write> {
    output: O,
    indent: u8,
}

macro_rules! writeln {
    ($self: ident, $($arg:tt)*) => (
        $self.writeln(format_args!($($arg)*))
    );
}

impl<O: Write> Printer<O> {
    pub fn new(output: O) -> Self {
        Self { output, indent: 0 }
    }

    fn indented<F: FnOnce(&mut Self) -> Result<()>>(&mut self, inner: F) -> Result<()> {
        self.indent += 2;
        let result = inner(self);
        self.indent -= 2;
        result
    }

    pub fn print_program(&mut self, program: Program) -> Result<()> {
        for node in program.nodes {
            self.print_node(node)?
        }
        Ok(())
    }

    fn print_node(&mut self, node: Node) -> Result<()> {
        match node {
            Node::Assignment(assignment) => self.print_assignment(assignment),
            Node::Function(function) => self.print_function(function),
            Node::Identifier(identifier) => self.print_identifier(identifier),
            Node::Integer(integer) => self.print_integer(integer),
            Node::PostfixCall(postfix_call) => self.print_postfix_call(postfix_call),
            Node::Return(ret) => self.print_return(ret),
            Node::Var(var) => self.print_var(var),
            _ => writeln!(self, "{:?}", node),
        }
    }

    fn print_assignment(&mut self, assignment: Assignment) -> Result<()> {
        writeln!(self, "Assignment(")?;
        self.indented(|printer| {
            printer.print_node(*assignment.lhs)?;
            printer.print_node(*assignment.rhs)
        })?;
        writeln!(self, ")")
    }

    fn print_block(&mut self, block: Block) -> Result<()> {
        writeln!(self, "Block(")?;
        self.indented(|printer| {
            for node in block.nodes {
                printer.print_node(node)?;
            }
            Ok(())
        })?;
        writeln!(self, ")")
    }

    fn print_function(&mut self, function: Function) -> Result<()> {
        writeln!(self, "Function({}", function.name)?;
        self.indented(|printer| {
            printer.print_block(function.block)
        })?;
        writeln!(self, ")")
    }

    fn print_identifier(&mut self, identifier: Identifier) -> Result<()> {
        writeln!(self, "Identifier({})", identifier.value)
    }

    fn print_integer(&mut self, integer: Integer) -> Result<()> {
        writeln!(self, "Integer({})", integer.value)
    }

    fn print_postfix_call(&mut self, postfix_call: PostfixCall) -> Result<()> {
        writeln!(self, "PostfixCall(")?;
        self.indented(|printer| {
            printer.print_node(*postfix_call.clone().target)?;
            if postfix_call.arguments.is_empty() {
                return writeln!(printer, "[]")
            }
            writeln!(printer, "[")?;
            printer.indented(|arguments_printer| {
                for argument in postfix_call.arguments {
                    arguments_printer.print_node(argument)?;
                }
                Ok(())
            })?;
            writeln!(printer, "]")
        })?;
        writeln!(self, ")")
    }

    fn print_return(&mut self, ret: Return) -> Result<()> {
        if let Some(rhs) = ret.rhs {
            writeln!(self, "Return(")?;
            self.indented(|printer| {
                printer.print_node(*rhs)
            })?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Return()")
        }
    }

    fn print_var(&mut self, var: Var) -> Result<()> {
        if let Some(rhs) = var.rhs {
            writeln!(self, "Var({}", var.lhs.value)?;
            self.indented(|printer| {
                printer.print_node(*rhs)
            })?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Var({})", var.lhs.value)
        }
    }

    fn writeln(&mut self, args: fmt::Arguments) -> Result<()> {
        let indented = " ".repeat(self.indent as usize);
        self.output
            .write(indented.as_bytes())
            .and_then(|_| self.output.write_fmt(args))
            .and_then(|_| self.output.write("\n".as_bytes()))
            .map(|_| ())
    }
}
<commit_msg>Format file<commit_after>use std::fmt;
use std::io::{Result, Write};

use super::nodes::*;

pub struct Printer<O: Write> {
    output: O,
    indent: u8,
}

macro_rules! writeln {
    ($self: ident, $($arg:tt)*) => (
        $self.writeln(format_args!($($arg)*))
    );
}

impl<O: Write> Printer<O> {
    pub fn new(output: O) -> Self {
        Self { output, indent: 0 }
    }

    fn indented<F: FnOnce(&mut Self) -> Result<()>>(&mut self, inner: F) -> Result<()> {
        self.indent += 2;
        let result = inner(self);
        self.indent -= 2;
        result
    }

    pub fn print_program(&mut self, program: Program) -> Result<()> {
        for node in program.nodes {
            self.print_node(node)?
        }
        Ok(())
    }

    fn print_node(&mut self, node: Node) -> Result<()> {
        match node {
            Node::Assignment(assignment) => self.print_assignment(assignment),
            Node::Function(function) => self.print_function(function),
            Node::Identifier(identifier) => self.print_identifier(identifier),
            Node::Integer(integer) => self.print_integer(integer),
            Node::PostfixCall(postfix_call) => self.print_postfix_call(postfix_call),
            Node::Return(ret) => self.print_return(ret),
            Node::Var(var) => self.print_var(var),
            _ => writeln!(self, "{:?}", node),
        }
    }

    fn print_assignment(&mut self, assignment: Assignment) -> Result<()> {
        writeln!(self, "Assignment(")?;
        self.indented(|printer| {
            printer.print_node(*assignment.lhs)?;
            printer.print_node(*assignment.rhs)
        })?;
        writeln!(self, ")")
    }

    fn print_block(&mut self, block: Block) -> Result<()> {
        writeln!(self, "Block(")?;
        self.indented(|printer| {
            for node in block.nodes {
                printer.print_node(node)?;
            }
            Ok(())
        })?;
        writeln!(self, ")")
    }

    fn print_function(&mut self, function: Function) -> Result<()> {
        writeln!(self, "Function({}", function.name)?;
        self.indented(|printer| printer.print_block(function.block))?;
        writeln!(self, ")")
    }

    fn print_identifier(&mut self, identifier: Identifier) -> Result<()> {
        writeln!(self, "Identifier({})", identifier.value)
    }

    fn print_integer(&mut self, integer: Integer) -> Result<()> {
        writeln!(self, "Integer({})", integer.value)
    }

    fn print_postfix_call(&mut self, postfix_call: PostfixCall) -> Result<()> {
        writeln!(self, "PostfixCall(")?;
        self.indented(|printer| {
            printer.print_node(*postfix_call.clone().target)?;
            if postfix_call.arguments.is_empty() {
                return writeln!(printer, "[]");
            }
            writeln!(printer, "[")?;
            printer.indented(|arguments_printer| {
                for argument in postfix_call.arguments {
                    arguments_printer.print_node(argument)?;
                }
                Ok(())
            })?;
            writeln!(printer, "]")
        })?;
        writeln!(self, ")")
    }

    fn print_return(&mut self, ret: Return) -> Result<()> {
        if let Some(rhs) = ret.rhs {
            writeln!(self, "Return(")?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Return()")
        }
    }

    fn print_var(&mut self, var: Var) -> Result<()> {
        if let Some(rhs) = var.rhs {
            writeln!(self, "Var({}", var.lhs.value)?;
            self.indented(|printer| printer.print_node(*rhs))?;
            writeln!(self, ")")
        } else {
            writeln!(self, "Var({})", var.lhs.value)
        }
    }

    fn writeln(&mut self, args: fmt::Arguments) -> Result<()> {
        let indented = " ".repeat(self.indent as usize);
        self.output
            .write(indented.as_bytes())
            .and_then(|_| self.output.write_fmt(args))
            .and_then(|_| self.output.write("\n".as_bytes()))
            .map(|_| ())
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};

use ra_db::SourceDatabaseExt;
use ra_hir::{AssocItem, Crate, HasSource, HirDisplay, ModuleDef, Ty, TypeWalk};
use ra_syntax::AstNode;

use crate::{progress_bar::ProgressBar, Result, Verbosity};

pub fn run(
    verbosity: Verbosity,
    memory_usage: bool,
    path: &Path,
    only: Option<&str>,
    with_deps: bool,
) -> Result<()> {
    let db_load_time = Instant::now();
    let (mut host, roots) = ra_batch::load_cargo(path)?;
    let db = host.raw_database();
    println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
    let analysis_time = Instant::now();
    let mut num_crates = 0;
    let mut visited_modules = HashSet::new();
    let mut visit_queue = Vec::new();

    let members =
        roots
            .into_iter()
            .filter_map(|(source_root_id, project_root)| {
                if with_deps || project_root.is_member() {
                    Some(source_root_id)
                } else {
                    None
                }
            })
            .collect::<HashSet<_>>();

    for krate in Crate::all(db) {
        let module = krate.root_module(db).expect("crate without root module");
        let file_id = module.definition_source(db).file_id;
        if members.contains(&db.file_source_root(file_id.original_file(db))) {
            num_crates += 1;
            visit_queue.push(module);
        }
    }

    println!("Crates in this dir: {}", num_crates);
    let mut num_decls = 0;
    let mut funcs = Vec::new();
    while let Some(module) = visit_queue.pop() {
        if visited_modules.insert(module) {
            visit_queue.extend(module.children(db));

            for decl in module.declarations(db) {
                num_decls += 1;
                if let ModuleDef::Function(f) = decl {
                    funcs.push(f);
                }
            }

            for impl_block in module.impl_blocks(db) {
                for item in impl_block.items(db) {
                    num_decls += 1;
                    if let AssocItem::Function(f) = item {
                        funcs.push(f);
                    }
                }
            }
        }
    }
    println!("Total modules found: {}", visited_modules.len());
    println!("Total declarations: {}", num_decls);
    println!("Total functions: {}", funcs.len());
    println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());

    let inference_time = Instant::now();
    let mut bar = match verbosity {
        Verbosity::Verbose | Verbosity::Normal => ProgressBar::new(funcs.len() as u64),
        Verbosity::Quiet => ProgressBar::hidden(),
    };

    // bar.set_style(
    //     indicatif::ProgressStyle::default_bar().template("{wide_bar} {pos}/{len}\n{msg}"),
    // );
    bar.tick();
    let mut num_exprs = 0;
    let mut num_exprs_unknown = 0;
    let mut num_exprs_partially_unknown = 0;
    let mut num_type_mismatches = 0;
    for f in funcs {
        let name = f.name(db);
        let mut msg = format!("processing: {}", name);
        if verbosity.is_verbose() {
            let src = f.source(db);
            let original_file = src.file_id.original_file(db);
            let path = db.file_relative_path(original_file);
            let syntax_range = src.value.syntax().text_range();
            write!(msg, " ({:?} {})", path, syntax_range).unwrap();
        }
        bar.set_message(&msg);
        if let Some(only_name) = only {
            if name.to_string() != only_name {
                continue;
            }
        }
        let body = f.body(db);
        let inference_result = f.infer(db);
        for (expr_id, _) in body.exprs.iter() {
            let ty = &inference_result[expr_id];
            num_exprs += 1;
            if let Ty::Unknown = ty {
                num_exprs_unknown += 1;
            } else {
                let mut is_partially_unknown = false;
                ty.walk(&mut |ty| {
                    if let Ty::Unknown = ty {
                        is_partially_unknown = true;
                    }
                });
                if is_partially_unknown {
                    num_exprs_partially_unknown += 1;
                }
            }
            if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                num_type_mismatches += 1;
                if verbosity.is_verbose() {
                    let src = f.body_source_map(db).expr_syntax(expr_id);
                    if let Some(src) = src {
                        // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
                        let original_file = src.file_id.original_file(db);
                        let path = db.file_relative_path(original_file);
                        let line_index = host.analysis().file_line_index(original_file).unwrap();
                        let text_range = src.value.either(
                            |it| it.syntax_node_ptr().range(),
                            |it| it.syntax_node_ptr().range(),
                        );
                        let (start, end) = (
                            line_index.line_col(text_range.start()),
                            line_index.line_col(text_range.end()),
                        );
                        bar.println(format!(
                            "{} {}:{}-{}:{}: Expected {}, got {}",
                            path,
                            start.line + 1,
                            start.col_utf16,
                            end.line + 1,
                            end.col_utf16,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    } else {
                        bar.println(format!(
                            "{}: Expected {}, got {}",
                            name,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    }
                }
            }
        }
        bar.inc(1);
    }
    bar.finish_and_clear();
    println!("Total expressions: {}", num_exprs);
    println!(
        "Expressions of unknown type: {} ({}%)",
        num_exprs_unknown,
        if num_exprs > 0 { (num_exprs_unknown * 100 / num_exprs) } else { 100 }
    );
    println!(
        "Expressions of partially unknown type: {} ({}%)",
        num_exprs_partially_unknown,
        if num_exprs > 0 { (num_exprs_partially_unknown * 100 / num_exprs) } else { 100 }
    );
    println!("Type mismatches: {}", num_type_mismatches);
    println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
    println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());

    if memory_usage {
        drop(db);
        for (name, bytes) in host.per_query_memory_usage() {
            println!("{:>8} {}", bytes, name)
        }
        let before = ra_prof::memory_usage();
        drop(host);
        println!("leftover: {}", before.allocated - ra_prof::memory_usage().allocated)
    }

    Ok(())
}
<commit_msg>Remove commented code<commit_after>//! FIXME: write short doc here

use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};

use ra_db::SourceDatabaseExt;
use ra_hir::{AssocItem, Crate, HasSource, HirDisplay, ModuleDef, Ty, TypeWalk};
use ra_syntax::AstNode;

use crate::{progress_bar::ProgressBar, Result, Verbosity};

pub fn run(
    verbosity: Verbosity,
    memory_usage: bool,
    path: &Path,
    only: Option<&str>,
    with_deps: bool,
) -> Result<()> {
    let db_load_time = Instant::now();
    let (mut host, roots) = ra_batch::load_cargo(path)?;
    let db = host.raw_database();
    println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
    let analysis_time = Instant::now();
    let mut num_crates = 0;
    let mut visited_modules = HashSet::new();
    let mut visit_queue = Vec::new();

    let members =
        roots
            .into_iter()
            .filter_map(|(source_root_id, project_root)| {
                if with_deps || project_root.is_member() {
                    Some(source_root_id)
                } else {
                    None
                }
            })
            .collect::<HashSet<_>>();

    for krate in Crate::all(db) {
        let module = krate.root_module(db).expect("crate without root module");
        let file_id = module.definition_source(db).file_id;
        if members.contains(&db.file_source_root(file_id.original_file(db))) {
            num_crates += 1;
            visit_queue.push(module);
        }
    }

    println!("Crates in this dir: {}", num_crates);
    let mut num_decls = 0;
    let mut funcs = Vec::new();
    while let Some(module) = visit_queue.pop() {
        if visited_modules.insert(module) {
            visit_queue.extend(module.children(db));

            for decl in module.declarations(db) {
                num_decls += 1;
                if let ModuleDef::Function(f) = decl {
                    funcs.push(f);
                }
            }

            for impl_block in module.impl_blocks(db) {
                for item in impl_block.items(db) {
                    num_decls += 1;
                    if let AssocItem::Function(f) = item {
                        funcs.push(f);
                    }
                }
            }
        }
    }
    println!("Total modules found: {}", visited_modules.len());
    println!("Total declarations: {}", num_decls);
    println!("Total functions: {}", funcs.len());
    println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());

    let inference_time = Instant::now();
    let mut bar = match verbosity {
        Verbosity::Verbose | Verbosity::Normal => ProgressBar::new(funcs.len() as u64),
        Verbosity::Quiet => ProgressBar::hidden(),
    };

    bar.tick();
    let mut num_exprs = 0;
    let mut num_exprs_unknown = 0;
    let mut num_exprs_partially_unknown = 0;
    let mut num_type_mismatches = 0;
    for f in funcs {
        let name = f.name(db);
        let mut msg = format!("processing: {}", name);
        if verbosity.is_verbose() {
            let src = f.source(db);
            let original_file = src.file_id.original_file(db);
            let path = db.file_relative_path(original_file);
            let syntax_range = src.value.syntax().text_range();
            write!(msg, " ({:?} {})", path, syntax_range).unwrap();
        }
        bar.set_message(&msg);
        if let Some(only_name) = only {
            if name.to_string() != only_name {
                continue;
            }
        }
        let body = f.body(db);
        let inference_result = f.infer(db);
        for (expr_id, _) in body.exprs.iter() {
            let ty = &inference_result[expr_id];
            num_exprs += 1;
            if let Ty::Unknown = ty {
                num_exprs_unknown += 1;
            } else {
                let mut is_partially_unknown = false;
                ty.walk(&mut |ty| {
                    if let Ty::Unknown = ty {
                        is_partially_unknown = true;
                    }
                });
                if is_partially_unknown {
                    num_exprs_partially_unknown += 1;
                }
            }
            if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                num_type_mismatches += 1;
                if verbosity.is_verbose() {
                    let src = f.body_source_map(db).expr_syntax(expr_id);
                    if let Some(src) = src {
                        // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
                        let original_file = src.file_id.original_file(db);
                        let path = db.file_relative_path(original_file);
                        let line_index = host.analysis().file_line_index(original_file).unwrap();
                        let text_range = src.value.either(
                            |it| it.syntax_node_ptr().range(),
                            |it| it.syntax_node_ptr().range(),
                        );
                        let (start, end) = (
                            line_index.line_col(text_range.start()),
                            line_index.line_col(text_range.end()),
                        );
                        bar.println(format!(
                            "{} {}:{}-{}:{}: Expected {}, got {}",
                            path,
                            start.line + 1,
                            start.col_utf16,
                            end.line + 1,
                            end.col_utf16,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    } else {
                        bar.println(format!(
                            "{}: Expected {}, got {}",
                            name,
                            mismatch.expected.display(db),
                            mismatch.actual.display(db)
                        ));
                    }
                }
            }
        }
        bar.inc(1);
    }
    bar.finish_and_clear();
    println!("Total expressions: {}", num_exprs);
    println!(
        "Expressions of unknown type: {} ({}%)",
        num_exprs_unknown,
        if num_exprs > 0 { (num_exprs_unknown * 100 / num_exprs) } else { 100 }
    );
    println!(
        "Expressions of partially unknown type: {} ({}%)",
        num_exprs_partially_unknown,
        if num_exprs > 0 { (num_exprs_partially_unknown * 100 / num_exprs) } else { 100 }
    );
    println!("Type mismatches: {}", num_type_mismatches);
    println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
    println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());

    if memory_usage {
        drop(db);
        for (name, bytes) in host.per_query_memory_usage() {
            println!("{:>8} {}", bytes, name)
        }
        let before = ra_prof::memory_usage();
        drop(host);
        println!("leftover: {}", before.allocated - ra_prof::memory_usage().allocated)
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! See [RequestDispatcher].
use std::{fmt, panic, thread};

use lsp_server::ExtractError;
use serde::{de::DeserializeOwned, Serialize};

use crate::{
    global_state::{GlobalState, GlobalStateSnapshot},
    lsp_utils::is_cancelled,
    main_loop::Task,
    LspError, Result,
};

/// A visitor for routing a raw JSON request to an appropriate handler function.
///
/// Most requests are read-only and async and are handled on the threadpool
/// (`on` method).
///
/// Some read-only requests are latency sensitive, and are immediately handled
/// on the main loop thread (`on_sync`). These are typically typing-related
/// requests.
///
/// Some requests modify the state, and are run on the main thread to get
/// `&mut` (`on_sync_mut`).
///
/// Read-only requests are wrapped into `catch_unwind` -- they don't modify the
/// state, so it's OK to recover from their failures.
pub(crate) struct RequestDispatcher<'a> {
    pub(crate) req: Option<lsp_server::Request>,
    pub(crate) global_state: &'a mut GlobalState,
}

impl<'a> RequestDispatcher<'a> {
    /// Dispatches the request onto the current thread, given full access to
    /// mutable global state. Unlike all other methods here, this one isn't
    /// guarded by `catch_unwind`, so, please, don't make bugs :-)
    pub(crate) fn on_sync_mut<R>(
        &mut self,
        f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
    ) -> Result<&mut Self>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return Ok(self),
        };
        let _pctx = stdx::panic_context::enter(panic_context);

        let result = f(self.global_state, params);
        let response = result_to_response::<R>(id, result);

        self.global_state.respond(response);
        Ok(self)
    }

    /// Dispatches the request onto the current thread.
    pub(crate) fn on_sync<R>(
        &mut self,
        f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
    ) -> Result<&mut Self>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return Ok(self),
        };
        let global_state_snapshot = self.global_state.snapshot();

        let result = panic::catch_unwind(move || {
            let _pctx = stdx::panic_context::enter(panic_context);
            f(global_state_snapshot, params)
        });
        let response = thread_result_to_response::<R>(id, result);

        self.global_state.respond(response);
        Ok(self)
    }

    /// Dispatches the request onto thread pool
    pub(crate) fn on<R>(
        &mut self,
        f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
    ) -> &mut Self
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return self,
        };

        self.global_state.task_pool.handle.spawn({
            let world = self.global_state.snapshot();
            move || {
                let result = panic::catch_unwind(move || {
                    let _pctx = stdx::panic_context::enter(panic_context);
                    f(world, params)
                });
                let response = thread_result_to_response::<R>(id, result);
                Task::Response(response)
            }
        });

        self
    }

    pub(crate) fn finish(&mut self) {
        if let Some(req) = self.req.take() {
            tracing::error!("unknown request: {:?}", req);
            let response = lsp_server::Response::new_err(
                req.id,
                lsp_server::ErrorCode::MethodNotFound as i32,
                "unknown request".to_string(),
            );
            self.global_state.respond(response);
        }
    }

    fn parse<R>(&mut self) -> Option<(lsp_server::RequestId, R::Params, String)>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + fmt::Debug + 'static,
    {
        let req = match &self.req {
            Some(req) if req.method == R::METHOD => self.req.take().unwrap(),
            _ => return None,
        };

        let res = crate::from_json(R::METHOD, req.params);
        match res {
            Ok(params) => {
                let panic_context =
                    format!("\nversion: {}\nrequest: {} {:#?}", env!("REV"), R::METHOD, params);
                Some((req.id, params, panic_context))
            }
            Err(err) => {
                let response = lsp_server::Response::new_err(
                    req.id,
                    lsp_server::ErrorCode::InvalidParams as i32,
                    err.to_string(),
                );
                self.global_state.respond(response);
                None
            }
        }
    }
}

fn thread_result_to_response<R>(
    id: lsp_server::RequestId,
    result: thread::Result<Result<R::Result>>,
) -> lsp_server::Response
where
    R: lsp_types::request::Request + 'static,
    R::Params: DeserializeOwned + 'static,
    R::Result: Serialize + 'static,
{
    match result {
        Ok(result) => result_to_response::<R>(id, result),
        Err(panic) => {
            let mut message = "server panicked".to_string();

            let panic_message = panic
                .downcast_ref::<String>()
                .map(String::as_str)
                .or_else(|| panic.downcast_ref::<&str>().copied());

            if let Some(panic_message) = panic_message {
                message.push_str(": ");
                message.push_str(panic_message)
            };

            lsp_server::Response::new_err(id, lsp_server::ErrorCode::InternalError as i32, message)
        }
    }
}

fn result_to_response<R>(
    id: lsp_server::RequestId,
    result: Result<R::Result>,
) -> lsp_server::Response
where
    R: lsp_types::request::Request + 'static,
    R::Params: DeserializeOwned + 'static,
    R::Result: Serialize + 'static,
{
    match result {
        Ok(resp) => lsp_server::Response::new_ok(id, &resp),
        Err(e) => match e.downcast::<LspError>() {
            Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
            Err(e) => {
                if is_cancelled(&*e) {
                    lsp_server::Response::new_err(
                        id,
                        lsp_server::ErrorCode::ContentModified as i32,
                        "content modified".to_string(),
                    )
                } else {
                    lsp_server::Response::new_err(
                        id,
                        lsp_server::ErrorCode::InternalError as i32,
                        e.to_string(),
                    )
                }
            }
        },
    }
}

pub(crate) struct NotificationDispatcher<'a> {
    pub(crate) not: Option<lsp_server::Notification>,
    pub(crate) global_state: &'a mut GlobalState,
}

impl<'a> NotificationDispatcher<'a> {
    pub(crate) fn on<N>(
        &mut self,
        f: fn(&mut GlobalState, N::Params) -> Result<()>,
    ) -> Result<&mut Self>
    where
        N: lsp_types::notification::Notification + 'static,
        N::Params: DeserializeOwned + Send + 'static,
    {
        let not = match self.not.take() {
            Some(it) => it,
            None => return Ok(self),
        };
        let params = match not.extract::<N::Params>(N::METHOD) {
            Ok(it) => it,
            Err(ExtractError::JsonError { method, error }) => {
                panic!("Invalid request\nMethod: {method}\n error: {error}",)
            }
            Err(ExtractError::MethodMismatch(not)) => {
                self.not = Some(not);
                return Ok(self);
            }
        };
        let _pctx = stdx::panic_context::enter(format!(
            "\nversion: {}\nnotification: {}",
            env!("REV"),
            N::METHOD
        ));
        f(self.global_state, params)?;
        Ok(self)
    }

    pub(crate) fn finish(&mut self) {
        if let Some(not) = &self.not {
            if !not.method.starts_with("$/") {
                tracing::error!("unhandled notification: {:?}", not);
            }
        }
    }
}
<commit_msg>Auto merge of #12070 - rust-lang:message, r=matklad<commit_after>//! See [RequestDispatcher].
use std::{fmt, panic, thread};

use lsp_server::ExtractError;
use serde::{de::DeserializeOwned, Serialize};

use crate::{
    global_state::{GlobalState, GlobalStateSnapshot},
    lsp_utils::is_cancelled,
    main_loop::Task,
    LspError, Result,
};

/// A visitor for routing a raw JSON request to an appropriate handler function.
///
/// Most requests are read-only and async and are handled on the threadpool
/// (`on` method).
///
/// Some read-only requests are latency sensitive, and are immediately handled
/// on the main loop thread (`on_sync`). These are typically typing-related
/// requests.
///
/// Some requests modify the state, and are run on the main thread to get
/// `&mut` (`on_sync_mut`).
///
/// Read-only requests are wrapped into `catch_unwind` -- they don't modify the
/// state, so it's OK to recover from their failures.
pub(crate) struct RequestDispatcher<'a> {
    pub(crate) req: Option<lsp_server::Request>,
    pub(crate) global_state: &'a mut GlobalState,
}

impl<'a> RequestDispatcher<'a> {
    /// Dispatches the request onto the current thread, given full access to
    /// mutable global state. Unlike all other methods here, this one isn't
    /// guarded by `catch_unwind`, so, please, don't make bugs :-)
    pub(crate) fn on_sync_mut<R>(
        &mut self,
        f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
    ) -> Result<&mut Self>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return Ok(self),
        };
        let _pctx = stdx::panic_context::enter(panic_context);

        let result = f(self.global_state, params);
        let response = result_to_response::<R>(id, result);

        self.global_state.respond(response);
        Ok(self)
    }

    /// Dispatches the request onto the current thread.
    pub(crate) fn on_sync<R>(
        &mut self,
        f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
    ) -> Result<&mut Self>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return Ok(self),
        };
        let global_state_snapshot = self.global_state.snapshot();

        let result = panic::catch_unwind(move || {
            let _pctx = stdx::panic_context::enter(panic_context);
            f(global_state_snapshot, params)
        });
        let response = thread_result_to_response::<R>(id, result);

        self.global_state.respond(response);
        Ok(self)
    }

    /// Dispatches the request onto thread pool
    pub(crate) fn on<R>(
        &mut self,
        f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
    ) -> &mut Self
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug + 'static,
        R::Result: Serialize + 'static,
    {
        let (id, params, panic_context) = match self.parse::<R>() {
            Some(it) => it,
            None => return self,
        };

        self.global_state.task_pool.handle.spawn({
            let world = self.global_state.snapshot();
            move || {
                let result = panic::catch_unwind(move || {
                    let _pctx = stdx::panic_context::enter(panic_context);
                    f(world, params)
                });
                let response = thread_result_to_response::<R>(id, result);
                Task::Response(response)
            }
        });

        self
    }

    pub(crate) fn finish(&mut self) {
        if let Some(req) = self.req.take() {
            tracing::error!("unknown request: {:?}", req);
            let response = lsp_server::Response::new_err(
                req.id,
                lsp_server::ErrorCode::MethodNotFound as i32,
                "unknown request".to_string(),
            );
            self.global_state.respond(response);
        }
    }

    fn parse<R>(&mut self) -> Option<(lsp_server::RequestId, R::Params, String)>
    where
        R: lsp_types::request::Request + 'static,
        R::Params: DeserializeOwned + fmt::Debug + 'static,
    {
        let req = match &self.req {
            Some(req) if req.method == R::METHOD => self.req.take().unwrap(),
            _ => return None,
        };

        let res = crate::from_json(R::METHOD, req.params);
        match res {
            Ok(params) => {
                let panic_context =
                    format!("\nversion: {}\nrequest: {} {:#?}", env!("REV"), R::METHOD, params);
                Some((req.id, params, panic_context))
            }
            Err(err) => {
                let response = lsp_server::Response::new_err(
                    req.id,
                    lsp_server::ErrorCode::InvalidParams as i32,
                    err.to_string(),
                );
                self.global_state.respond(response);
                None
            }
        }
    }
}

fn thread_result_to_response<R>(
    id: lsp_server::RequestId,
    result: thread::Result<Result<R::Result>>,
) -> lsp_server::Response
where
    R: lsp_types::request::Request + 'static,
    R::Params: DeserializeOwned + 'static,
    R::Result: Serialize + 'static,
{
    match result {
        Ok(result) => result_to_response::<R>(id, result),
        Err(panic) => {
            let mut message = "request handler panicked".to_string();

            let panic_message = panic
                .downcast_ref::<String>()
                .map(String::as_str)
                .or_else(|| panic.downcast_ref::<&str>().copied());

            if let Some(panic_message) = panic_message {
                message.push_str(": ");
                message.push_str(panic_message)
            };

            lsp_server::Response::new_err(id, lsp_server::ErrorCode::InternalError as i32, message)
        }
    }
}

fn result_to_response<R>(
    id: lsp_server::RequestId,
    result: Result<R::Result>,
) -> lsp_server::Response
where
    R: lsp_types::request::Request + 'static,
    R::Params: DeserializeOwned + 'static,
    R::Result: Serialize + 'static,
{
    match result {
        Ok(resp) => lsp_server::Response::new_ok(id, &resp),
        Err(e) => match e.downcast::<LspError>() {
            Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
            Err(e) => {
                if is_cancelled(&*e) {
                    lsp_server::Response::new_err(
                        id,
                        lsp_server::ErrorCode::ContentModified as i32,
                        "content modified".to_string(),
                    )
                } else {
                    lsp_server::Response::new_err(
                        id,
                        lsp_server::ErrorCode::InternalError as i32,
                        e.to_string(),
                    )
                }
            }
        },
    }
}

pub(crate) struct NotificationDispatcher<'a> {
    pub(crate) not: Option<lsp_server::Notification>,
    pub(crate) global_state: &'a mut GlobalState,
}

impl<'a> NotificationDispatcher<'a> {
    pub(crate) fn on<N>(
        &mut self,
        f: fn(&mut GlobalState, N::Params) -> Result<()>,
    ) -> Result<&mut Self>
    where
        N: lsp_types::notification::Notification + 'static,
        N::Params: DeserializeOwned + Send + 'static,
    {
        let not = match self.not.take() {
            Some(it) => it,
            None => return Ok(self),
        };
        let params = match not.extract::<N::Params>(N::METHOD) {
            Ok(it) => it,
            Err(ExtractError::JsonError { method, error }) => {
                panic!("Invalid request\nMethod: {method}\n error: {error}",)
            }
            Err(ExtractError::MethodMismatch(not)) => {
                self.not = Some(not);
                return Ok(self);
            }
        };
        let _pctx = stdx::panic_context::enter(format!(
            "\nversion: {}\nnotification: {}",
            env!("REV"),
            N::METHOD
        ));
        f(self.global_state, params)?;
        Ok(self)
    }

    pub(crate) fn finish(&mut self) {
        if let Some(not) = &self.not {
            if !not.method.starts_with("$/") {
                tracing::error!("unhandled notification: {:?}", not);
            }
        }
    }
}
<|endoftext|>