<commit_before>use {Error, Matrix, Result, Scan, ScanPosition};
use nalgebra::Eye;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use xml::reader::{EventReader, XmlEvent};

/// A RiSCAN Pro project.
#[derive(Clone, Debug)]
pub struct Project {
    scan_positions: HashMap<String, ScanPosition>,
    path: Option<PathBuf>,
    pop: Matrix,
}

impl Project {
    /// Reads a project from a path.
    ///
    /// This path can either be the `.RiSCAN` directory, or the underlying `project.rsp` file.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let project = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        let mut path = path.as_ref().to_path_buf();
        if fs::metadata(&path)?.is_dir() {
            path.push("project.rsp");
        }
        let ref mut reader = EventReader::new(BufReader::new(File::open(&path)?));
        let mut project = Project::new();
        path.pop();
        project.path = Some(path);

        if let Some(name) = next_element_name(reader)? {
            if name != "project" {
                return Err(Error::XmlRead(format!("invalid root element: {}", name)));
            }
        } else {
            return Err(Error::XmlRead("empty xml file".to_string()));
        }

        while let Some(name) = next_element_name(reader)? {
            match name.as_str() {
                "pop" => {
                    project.set_pop(read_matrix(reader)?);
                }
                "scanpositions" => {
                    while let Some(scan_position) = read_scan_position(reader)? {
                        project.add_scan_position(scan_position);
                    }
                }
                _ => consume_branch(reader)?,
            }
        }

        Ok(project)
    }

    /// Creates a new, default project.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::new();
    /// ```
    pub fn new() -> Project {
        Project {
            scan_positions: HashMap::new(),
            path: None,
            pop: Matrix::new_identity(4),
        }
    }

    /// Returns this project's POP.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// # let mut project = Project::new();
    /// let pop = project.pop();
    /// ```
    pub fn pop(&self) -> Matrix {
        self.pop
    }

    /// Sets this project's POP matrix.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::{Project, Matrix};
    /// # let mut project = Project::new();
    /// project.set_pop(Matrix::new(1., 0., 0., 1.,
    ///                             0., 1., 0., 2.,
    ///                             0., 0., 1., 3.,
    ///                             0., 0., 0., 1.));
    /// ```
    pub fn set_pop(&mut self, pop: Matrix) {
        for scan_position in self.scan_positions.values_mut() {
            scan_position.set_pop(pop);
        }
        self.pop = pop;
    }

    /// Returns a scan position with the given name or scan.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_position = project.scan_position("SP01").unwrap();
    /// let scan_position = project.scan_position("151120_150404").unwrap();
    /// let scan_position = project.scan_position("SCANS/SP01/SINGLESCANS/151120_150404.rxp").unwrap();
    /// ```
    pub fn scan_position(&self, name: &str) -> Option<&ScanPosition> {
        self.scan_positions.get(name).or_else(|| {
            if let Some(file_name) = Path::new(name).file_name() {
                if let Some(name) = Path::new(file_name).file_stem() {
                    return self.scan_positions
                        .values()
                        .find(|scan_position| scan_position.scan(&name.to_string_lossy()).is_some());
                }
            }
            None
        })
    }

    /// Returns a vector of all the scan positions in this scan.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_positions = project.scan_positions();
    /// ```
    pub fn scan_positions(&self) -> Vec<&ScanPosition> {
        self.scan_positions.values().collect()
    }

    /// Adds a scan position to this project.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::{Project, ScanPosition};
    /// let mut project = Project::new();
    /// let scan_position = ScanPosition::new();
    /// project.add_scan_position(scan_position);
    /// ```
    pub fn add_scan_position(&mut self, mut scan_position: ScanPosition) {
        scan_position.set_pop(self.pop);
        self.scan_positions.insert(scan_position.name().to_string(), scan_position);
    }

    /// Returns this project's (optional) path as a reference.
    ///
    /// The project path is the `.RiSCAN` directory, not the XML file.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// assert_eq!("data/project.RiSCAN", project.path().unwrap().to_string_lossy());
    /// ```
    pub fn path(&self) -> Option<&Path> {
        self.path.as_ref().map(|path_buf| path_buf.as_path())
    }
}

fn next_element_name<R: Read>(reader: &mut EventReader<R>) -> Result<Option<String>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => return Ok(Some(name.local_name)),
            XmlEvent::EndDocument => return Ok(None),
            _ => {}
        }
    }
}

fn consume_branch<R: Read>(reader: &mut EventReader<R>) -> Result<()> {
    let mut depth = 0;
    loop {
        match reader.next()? {
            XmlEvent::StartElement { .. } => depth += 1,
            XmlEvent::EndElement { .. } => {
                if depth == 0 {
                    return Ok(());
                } else {
                    depth -= 1;
                }
            }
            _ => {}
        }
    }
}

fn read_matrix<R: Read>(reader: &mut EventReader<R>) -> Result<Matrix> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "matrix" => {
                        let matrix = read_characters(reader).and_then(|s| matrix_from_str(&s))?;
                        consume_branch(reader)?;
                        return Ok(matrix);
                    }
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => {
                return Err(Error::XmlRead("missing matrix element".to_string()));
            }
            _ => {}
        }
    }
}

fn read_scan_position<R: Read>(reader: &mut EventReader<R>) -> Result<Option<ScanPosition>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                if name.local_name != "scanposition" {
                    return Err(Error::XmlRead(format!("unexpected element name: {}", name)));
                }
                break;
            }
            XmlEvent::EndElement { .. } => return Ok(None),
            _ => {}
        }
    }
    let mut scan_position = ScanPosition::new();
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "name" => {
                        scan_position.set_name(&read_characters(reader)?);
                    }
                    "singlescans" => {
                        while let Some(scan) = read_scan(reader)? {
                            scan_position.add_scan(scan);
                        }
                    }
                    "sop" => scan_position.set_sop(read_matrix(reader)?),
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => break,
            _ => {}
        }
    }
    Ok(Some(scan_position))
}

fn read_scan<R: Read>(reader: &mut EventReader<R>) -> Result<Option<Scan>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                if name.local_name != "scan" {
                    return Err(Error::XmlRead(format!("unexpected element name: {}", name)));
                }
                break;
            }
            XmlEvent::EndElement { .. } => return Ok(None),
            _ => {}
        }
    }
    let mut scan = Scan::new();
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "name" => {
                        scan.set_name(&read_characters(reader)?);
                    }
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => break,
            _ => {}
        }
    }
    Ok(Some(scan))
}

fn read_characters<R: Read>(reader: &mut EventReader<R>) -> Result<String> {
    let s = match reader.next()? {
        XmlEvent::Characters(s) => s,
        event @ _ => return Err(Error::XmlRead(format!("expected characters, got {:?}", event))),
    };
    consume_branch(reader)?;
    Ok(s)
}

fn matrix_from_str(s: &str) -> Result<Matrix> {
    let entries = s.split_whitespace()
        .map(|s| s.parse().map_err(Error::from))
        .collect::<Result<Vec<_>>>()?;
    if entries.len() != 16 {
        return Err(Error::Matrix(entries));
    }
    Ok(Matrix::new(entries[0],
                   entries[1],
                   entries[2],
                   entries[3],
                   entries[4],
                   entries[5],
                   entries[6],
                   entries[7],
                   entries[8],
                   entries[9],
                   entries[10],
                   entries[11],
                   entries[12],
                   entries[13],
                   entries[14],
                   entries[15]))
}

#[cfg(test)]
mod tests {
    use {Matrix, ScanPosition};
    use nalgebra::Eye;
    use super::*;

    #[test]
    fn project_read_from() {
        let project = Project::from_path("data/project.RiSCAN").unwrap();
        assert_eq!(0.99566497679815923, project.pop()[(0, 0)]);
        assert_eq!(2, project.scan_positions().len());
        assert!(project.scan_position("SP01").is_some());
        assert!(project.scan_position("SP02").is_some());
        assert!(project.scan_position("SP03").is_none());
    }

    #[test]
    fn project_add_scan_position() {
        let mut pop = Matrix::new_identity(4);
        pop[(0, 3)] = 1.;
        let mut project = Project::new();
        project.set_pop(pop);
        let mut scan_position = ScanPosition::new();
        scan_position.set_name("ScanPos001");
        project.add_scan_position(scan_position);
        assert_eq!(pop, project.scan_position("ScanPos001").unwrap().pop());
    }

    #[test]
    fn project_set_pop() {
        let mut project = Project::new();
        let mut scan_position = ScanPosition::new();
        scan_position.set_name("ScanPos001");
        project.add_scan_position(scan_position);
        let mut pop = Matrix::new_identity(4);
        pop[(0, 3)] = 1.;
        project.set_pop(pop);
        assert_eq!(pop, project.scan_position("ScanPos001").unwrap().pop());

    }
}
<commit_msg>Allow multiple extensions on scan name<commit_after>use {Error, Matrix, Result, Scan, ScanPosition};
use nalgebra::Eye;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use xml::reader::{EventReader, XmlEvent};

/// A RiSCAN Pro project.
#[derive(Clone, Debug)]
pub struct Project {
    scan_positions: HashMap<String, ScanPosition>,
    path: Option<PathBuf>,
    pop: Matrix,
}

impl Project {
    /// Reads a project from a path.
    ///
    /// This path can either be the `.RiSCAN` directory, or the underlying `project.rsp` file.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let project = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Project> {
        let mut path = path.as_ref().to_path_buf();
        if fs::metadata(&path)?.is_dir() {
            path.push("project.rsp");
        }
        let ref mut reader = EventReader::new(BufReader::new(File::open(&path)?));
        let mut project = Project::new();
        path.pop();
        project.path = Some(path);

        if let Some(name) = next_element_name(reader)? {
            if name != "project" {
                return Err(Error::XmlRead(format!("invalid root element: {}", name)));
            }
        } else {
            return Err(Error::XmlRead("empty xml file".to_string()));
        }

        while let Some(name) = next_element_name(reader)? {
            match name.as_str() {
                "pop" => {
                    project.set_pop(read_matrix(reader)?);
                }
                "scanpositions" => {
                    while let Some(scan_position) = read_scan_position(reader)? {
                        project.add_scan_position(scan_position);
                    }
                }
                _ => consume_branch(reader)?,
            }
        }

        Ok(project)
    }

    /// Creates a new, default project.
    ///
    /// # Examples
    ///
    /// ```
    /// use riscan_pro::Project;
    /// let project = Project::new();
    /// ```
    pub fn new() -> Project {
        Project {
            scan_positions: HashMap::new(),
            path: None,
            pop: Matrix::new_identity(4),
        }
    }

    /// Returns this project's POP.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// # let mut project = Project::new();
    /// let pop = project.pop();
    /// ```
    pub fn pop(&self) -> Matrix {
        self.pop
    }

    /// Sets this project's POP matrix.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::{Project, Matrix};
    /// # let mut project = Project::new();
    /// project.set_pop(Matrix::new(1., 0., 0., 1.,
    ///                             0., 1., 0., 2.,
    ///                             0., 0., 1., 3.,
    ///                             0., 0., 0., 1.));
    /// ```
    pub fn set_pop(&mut self, pop: Matrix) {
        for scan_position in self.scan_positions.values_mut() {
            scan_position.set_pop(pop);
        }
        self.pop = pop;
    }

    /// Returns a scan position with the given name or scan.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// assert!(project.scan_position("SP01").is_some());
    /// assert!(project.scan_position("151120_150404").is_some());
    /// assert!(project.scan_position("SCANS/SP01/SINGLESCANS/151120_150404.rxp").is_some());
    /// assert!(project.scan_position("151120_150404.mta.rxp").is_some());
    /// ```
    pub fn scan_position(&self, name: &str) -> Option<&ScanPosition> {
        self.scan_positions.get(name).or_else(|| {
            if let Some(file_name) = Path::new(name).file_name() {
                if let Some(name) = file_name.to_string_lossy().split('.').next() {
                    return self.scan_positions
                        .values()
                        .find(|scan_position| scan_position.scan(name).is_some());
                }
            }
            None
        })
    }

    /// Returns a vector of all the scan positions in this scan.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN").unwrap();
    /// let scan_positions = project.scan_positions();
    /// ```
    pub fn scan_positions(&self) -> Vec<&ScanPosition> {
        self.scan_positions.values().collect()
    }

    /// Adds a scan position to this project.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::{Project, ScanPosition};
    /// let mut project = Project::new();
    /// let scan_position = ScanPosition::new();
    /// project.add_scan_position(scan_position);
    /// ```
    pub fn add_scan_position(&mut self, mut scan_position: ScanPosition) {
        scan_position.set_pop(self.pop);
        self.scan_positions.insert(scan_position.name().to_string(), scan_position);
    }

    /// Returns this project's (optional) path as a reference.
    ///
    /// The project path is the `.RiSCAN` directory, not the XML file.
    ///
    /// # Examples
    ///
    /// ```
    /// # use riscan_pro::Project;
    /// let project = Project::from_path("data/project.RiSCAN/project.rsp").unwrap();
    /// assert_eq!("data/project.RiSCAN", project.path().unwrap().to_string_lossy());
    /// ```
    pub fn path(&self) -> Option<&Path> {
        self.path.as_ref().map(|path_buf| path_buf.as_path())
    }
}

fn next_element_name<R: Read>(reader: &mut EventReader<R>) -> Result<Option<String>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => return Ok(Some(name.local_name)),
            XmlEvent::EndDocument => return Ok(None),
            _ => {}
        }
    }
}

fn consume_branch<R: Read>(reader: &mut EventReader<R>) -> Result<()> {
    let mut depth = 0;
    loop {
        match reader.next()? {
            XmlEvent::StartElement { .. } => depth += 1,
            XmlEvent::EndElement { .. } => {
                if depth == 0 {
                    return Ok(());
                } else {
                    depth -= 1;
                }
            }
            _ => {}
        }
    }
}

fn read_matrix<R: Read>(reader: &mut EventReader<R>) -> Result<Matrix> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "matrix" => {
                        let matrix = read_characters(reader).and_then(|s| matrix_from_str(&s))?;
                        consume_branch(reader)?;
                        return Ok(matrix);
                    }
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => {
                return Err(Error::XmlRead("missing matrix element".to_string()));
            }
            _ => {}
        }
    }
}

fn read_scan_position<R: Read>(reader: &mut EventReader<R>) -> Result<Option<ScanPosition>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                if name.local_name != "scanposition" {
                    return Err(Error::XmlRead(format!("unexpected element name: {}", name)));
                }
                break;
            }
            XmlEvent::EndElement { .. } => return Ok(None),
            _ => {}
        }
    }
    let mut scan_position = ScanPosition::new();
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "name" => {
                        scan_position.set_name(&read_characters(reader)?);
                    }
                    "singlescans" => {
                        while let Some(scan) = read_scan(reader)? {
                            scan_position.add_scan(scan);
                        }
                    }
                    "sop" => scan_position.set_sop(read_matrix(reader)?),
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => break,
            _ => {}
        }
    }
    Ok(Some(scan_position))
}

fn read_scan<R: Read>(reader: &mut EventReader<R>) -> Result<Option<Scan>> {
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                if name.local_name != "scan" {
                    return Err(Error::XmlRead(format!("unexpected element name: {}", name)));
                }
                break;
            }
            XmlEvent::EndElement { .. } => return Ok(None),
            _ => {}
        }
    }
    let mut scan = Scan::new();
    loop {
        match reader.next()? {
            XmlEvent::StartElement { name, .. } => {
                match name.local_name.as_str() {
                    "name" => {
                        scan.set_name(&read_characters(reader)?);
                    }
                    _ => consume_branch(reader)?,
                }
            }
            XmlEvent::EndElement { .. } => break,
            _ => {}
        }
    }
    Ok(Some(scan))
}

fn read_characters<R: Read>(reader: &mut EventReader<R>) -> Result<String> {
    let s = match reader.next()? {
        XmlEvent::Characters(s) => s,
        event @ _ => return Err(Error::XmlRead(format!("expected characters, got {:?}", event))),
    };
    consume_branch(reader)?;
    Ok(s)
}

fn matrix_from_str(s: &str) -> Result<Matrix> {
    let entries = s.split_whitespace()
        .map(|s| s.parse().map_err(Error::from))
        .collect::<Result<Vec<_>>>()?;
    if entries.len() != 16 {
        return Err(Error::Matrix(entries));
    }
    Ok(Matrix::new(entries[0],
                   entries[1],
                   entries[2],
                   entries[3],
                   entries[4],
                   entries[5],
                   entries[6],
                   entries[7],
                   entries[8],
                   entries[9],
                   entries[10],
                   entries[11],
                   entries[12],
                   entries[13],
                   entries[14],
                   entries[15]))
}

#[cfg(test)]
mod tests {
    use {Matrix, ScanPosition};
    use nalgebra::Eye;
    use super::*;

    #[test]
    fn project_read_from() {
        let project = Project::from_path("data/project.RiSCAN").unwrap();
        assert_eq!(0.99566497679815923, project.pop()[(0, 0)]);
        assert_eq!(2, project.scan_positions().len());
        assert!(project.scan_position("SP01").is_some());
        assert!(project.scan_position("SP02").is_some());
        assert!(project.scan_position("SP03").is_none());
    }

    #[test]
    fn project_add_scan_position() {
        let mut pop = Matrix::new_identity(4);
        pop[(0, 3)] = 1.;
        let mut project = Project::new();
        project.set_pop(pop);
        let mut scan_position = ScanPosition::new();
        scan_position.set_name("ScanPos001");
        project.add_scan_position(scan_position);
        assert_eq!(pop, project.scan_position("ScanPos001").unwrap().pop());
    }

    #[test]
    fn project_set_pop() {
        let mut project = Project::new();
        let mut scan_position = ScanPosition::new();
        scan_position.set_name("ScanPos001");
        project.add_scan_position(scan_position);
        let mut pop = Matrix::new_identity(4);
        pop[(0, 3)] = 1.;
        project.set_pop(pop);
        assert_eq!(pop, project.scan_position("ScanPos001").unwrap().pop());

    }
}
<|endoftext|>


<commit_before>use super::*;

pub trait Request {
    type Params;
    type Result;
    const METHOD: &'static str;
}

#[macro_export]
macro_rules! lsp_request {
    ("initialize") => { $crate::request::Initialize };
    ("shutdown") => { $crate::request::Shutdown };
    ("window/showMessageRequest") => { $crate::request::ShowMessageRequest };
    ("textDocument/willSaveWaitUntil") => { $crate::request::WillSaveWaitUntil };
    ("textDocument/completion") => { $crate::request::Completion };
    ("textDocument/hover") => { $crate::request::HoverRequest };
    ("textDocument/signatureHelp") => { $crate::request::SignatureHelp };
    ("textDocument/definition") => { $crate::request::Definition };
    ("textDocument/references") => { $crate::request::References };
    ("textDocument/documentHighlight") => { $crate::request::DocumentHighlightRequest };
    ("textDocument/documentSymbol") => { $crate::request::DocumentSymbol };
    ("textDocument/codeAction") => { $crate::request::CodeAction };
    ("textDocument/codeLens") => { $crate::request::CodeLensRequest };
    ("codeLens/resolve") => { $crate::request::CodeLensResolve };
    ("textDocument/documentLink") => { $crate::request::DocumentLink };
    ("textDocument/applyEdit") => { $crate::request::ApplyEdit };
    ("textDocument/rangeFormatting") => { $crate::request::RangeFormatting };
    ("textDocument/onTypeFormatting") => { $crate::request::OnTypeFormatting };
    ("textDocument/formatting") => { $crate::request::Formatting };
    ("textDocument/rename") => { $crate::request::Rename };
    ("completionItem/resolve") => { $crate::request::ResolveCompletionItem };
    ("documentLink/resolve") => { $crate::request::DocumentLinkResolve };
    ("workspace/symbol") => { $crate::request::WorkspaceSymbol };
    ("workspace/executeCommand") => { $crate::request::ExecuteCommand }
}

/**

 The initialize request is sent as the first request from the client to the server.
 If the server receives request or notification before the `initialize` request it should act as follows:

 * for a request the respond should be errored with `code: -32001`. The message can be picked by the server.
 * notifications should be dropped.

*/
#[derive(Debug)]
pub enum Initialize {}

impl Request for Initialize {
    type Params = InitializeParams;
    type Result = InitializeResult;
    const METHOD: &'static str = "initialize";
}

/**
 * The shutdown request is sent from the client to the server. It asks the server to shut down,
 * but to not exit (otherwise the response might not be delivered correctly to the client).
 * There is a separate exit notification that asks the server to exit.
 */
#[derive(Debug)]
pub enum Shutdown {}

impl Request for Shutdown {
    type Params = ();
    type Result = ();
    const METHOD: &'static str = "shutdown";
}

/**
 * The show message request is sent from a server to a client to ask the client to display a particular message
 * in the user interface. In addition to the show message notification the request allows to pass actions and to
 * wait for an answer from the client.
 */
#[derive(Debug)]
pub enum ShowMessageRequest {}

impl Request for ShowMessageRequest {
    type Params = ShowMessageRequestParams;
    type Result = Option<MessageActionItem>;
    const METHOD: &'static str = "window/showMessageRequest";
}

/**
 The Completion request is sent from the client to the server to compute completion items at a given cursor position.
 Completion items are presented in the IntelliSense user interface. If computing full completion items is expensive,
 servers can additionally provide a handler for the completion item resolve request ('completionItem/resolve').
 This request is sent when a completion item is selected in the user interface. A typically use case is for example:
 the 'textDocument/completion' request doesn't fill in the documentation property for returned completion items
 since it is expensive to compute. When the item is selected in the user interface then a 'completionItem/resolve'
 request is sent with the selected completion item as a param. The returned completion item should have the
 documentation property filled in.
*/
#[derive(Debug)]
pub enum Completion {}

impl Request for Completion {
    type Params = CompletionParams;
    type Result = CompletionResponse;
    const METHOD: &'static str = "textDocument/completion";
}

/// The request is sent from the client to the server to resolve additional information for a given completion item.
#[derive(Debug)]
pub enum ResolveCompletionItem {}

impl Request for ResolveCompletionItem {
    type Params = CompletionItem;
    type Result = CompletionItem;
    const METHOD: &'static str = "completionItem/resolve";
}

/// The hover request is sent from the client to the server to request hover information at a given text
/// document position.
#[derive(Debug)]
pub enum HoverRequest {}

impl Request for HoverRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Hover>;
    const METHOD: &'static str = "textDocument/hover";
}

/// The signature help request is sent from the client to the server to request signature information at
/// a given cursor position.
#[derive(Debug)]
pub enum SignatureHelpRequest {}

impl Request for SignatureHelpRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<SignatureHelp>;
    const METHOD: &'static str = "textDocument/signatureHelp";
}

/// The goto definition request is sent from the client to the server to resolve the definition location of
/// a symbol at a given text document position.
#[derive(Debug)]
pub enum GotoDefinition {}

impl Request for GotoDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoDefinitionResponse>;
    const METHOD: &'static str = "textDocument/definition";
}

/**
 * GotoDefinition response can be single location or multiple ones.
 */
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GotoDefinitionResponse {
    Scalar(Location),
    Array(Vec<Location>),
}

/// The references request is sent from the client to the server to resolve project-wide references for the
/// symbol denoted by the given text document position.
pub enum References {}

impl Request for References {
    type Params = ReferenceParams;
    type Result = Option<Vec<Location>>;
    const METHOD: &'static str = "textDocument/references";
}

/**
 The document highlight request is sent from the client to the server to resolve a document highlights
 for a given text document position.
 For programming languages this usually highlights all references to the symbol scoped to this file.
 However we kept 'textDocument/documentHighlight' and 'textDocument/references' separate requests since
 the first one is allowed to be more fuzzy.
 Symbol matches usually have a DocumentHighlightKind of Read or Write whereas fuzzy or textual matches
 use Textas the kind.
*/
#[derive(Debug)]
pub enum DocumentHighlightRequest {}

impl Request for DocumentHighlightRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Vec<DocumentHighlight>>;
    const METHOD: &'static str = "textDocument/documentHighlight";
}

/**
 * The document symbol request is sent from the client to the server to list all symbols found in a given
 * text document.
 */
#[derive(Debug)]
pub enum DocumentSymbol {}

impl Request for DocumentSymbol {
    type Params = DocumentSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "textDocument/documentSymbol";
}

/**
 * The workspace symbol request is sent from the client to the server to list project-wide symbols
 * matching the query string.
 */
#[derive(Debug)]
pub enum WorkspaceSymbol {}

impl Request for WorkspaceSymbol {
    type Params = WorkspaceSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "workspace/symbol";
}

/// The workspace/executeCommand request is sent from the client to the server to trigger command execution on the server. In most cases the server creates a WorkspaceEdit structure and applies the changes to the workspace using the request workspace/applyEdit which is sent from the server to the client.
#[derive(Debug)]
pub enum ExecuteCommand {}

impl Request for ExecuteCommand {
    type Params = ExecuteCommandParams;
    type Result = Option<Value>;
    const METHOD: &'static str = "workspace/executeCommand";
}

/// The workspace/applyEdit request is sent from the server to the client to modify resource on the
/// client side.
#[derive(Debug)]
pub enum ApplyWorkspaceEdit {}

impl Request for ApplyWorkspaceEdit {
    type Params = ApplyWorkspaceEditParams;
    type Result = ApplyWorkspaceEditResponse;
    const METHOD: &'static str = "workspace/applyEdit";
}

/**
 * The code action request is sent from the client to the server to compute commands for a given text document
 * and range. The request is triggered when the user moves the cursor into a problem marker in the editor or
 * presses the lightbulb associated with a marker.
 */
#[derive(Debug)]
pub enum CodeActionRequest {}

impl Request for CodeActionRequest {
    type Params = CodeActionParams;
    type Result = Option<Vec<Command>>;
    const METHOD: &'static str = "textDocument/codeAction";
}

/**
 * The code lens request is sent from the client to the server to compute code lenses for a given text document.
 */
#[derive(Debug)]
pub enum CodeLensRequest {}

impl Request for CodeLensRequest {
    type Params = CodeLensParams;
    type Result = Option<Vec<CodeLens>>;
    const METHOD: &'static str = "textDocument/codeLens";
}

/**
 * The code lens resolve request is sent from the client to the server to resolve the command for a
 * given code lens item.
 */
#[derive(Debug)]
pub enum CodeLensResolve {}

impl Request for CodeLensResolve {
    type Params = CodeLens;
    type Result = CodeLens;
    const METHOD: &'static str = "codeLens/resolve";
}

/// The document links request is sent from the client to the server to request the location of links in a document.
#[derive(Debug)]
pub enum DocumentLinkRequest {}

impl Request for DocumentLinkRequest {
    type Params = DocumentLinkParams;
    type Result = Option<Vec<DocumentLink>>;
    const METHOD: &'static str = "textDocument/documentLink";
}

/**

 The document link resolve request is sent from the client to the server to resolve the target of
 a given document link.

*/
#[derive(Debug)]
pub enum DocumentLinkResolve {}

impl Request for DocumentLinkResolve {
    type Params = DocumentLink;
    type Result = DocumentLink;
    const METHOD: &'static str = "documentLink/resolve";
}

/**
 * The document formatting request is sent from the server to the client to format a whole document.
 */
#[derive(Debug)]
pub enum Formatting {}

impl Request for Formatting {
    type Params = DocumentFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/formatting";
}

/// The document range formatting request is sent from the client to the server to format a given range in a document.
#[derive(Debug)]
pub enum RangeFormatting {}

impl Request for RangeFormatting {
    type Params = DocumentRangeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/rangeFormatting";
}

/**
 * The document on type formatting request is sent from the client to the server to format parts of
 * the document during typing.
 */
#[derive(Debug)]
pub enum OnTypeFormatting {}

impl Request for OnTypeFormatting {
    type Params = DocumentOnTypeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/onTypeFormatting";
}

/**
 * The rename request is sent from the client to the server to perform a workspace-wide rename of a symbol.
 */
#[derive(Debug)]
pub enum Rename {}

impl Request for Rename {
    type Params = RenameParams;
    type Result = Option<WorkspaceEdit>;
    const METHOD: &'static str = "textDocument/rename";
}
<commit_msg>Some fixes to the lsp_request macro<commit_after>use super::*;

pub trait Request {
    type Params;
    type Result;
    const METHOD: &'static str;
}

#[macro_export]
macro_rules! lsp_request {
    ("initialize") => { $crate::request::Initialize };
    ("shutdown") => { $crate::request::Shutdown };
    ("window/showMessageRequest") => { $crate::request::ShowMessageRequest };
    ("textDocument/willSaveWaitUntil") => { $crate::request::WillSaveWaitUntil };
    ("textDocument/completion") => { $crate::request::Completion };
    ("textDocument/hover") => { $crate::request::HoverRequest };
    ("textDocument/signatureHelp") => { $crate::request::SignatureHelpRequest };
    ("textDocument/definition") => { $crate::request::GotoDefinition };
    ("textDocument/references") => { $crate::request::References };
    ("textDocument/documentHighlight") => { $crate::request::DocumentHighlightRequest };
    ("textDocument/documentSymbol") => { $crate::request::DocumentSymbol };
    ("textDocument/codeAction") => { $crate::request::CodeAction };
    ("textDocument/codeLens") => { $crate::request::CodeLensRequest };
    ("codeLens/resolve") => { $crate::request::CodeLensResolve };
    ("textDocument/documentLink") => { $crate::request::DocumentLink };
    ("textDocument/applyEdit") => { $crate::request::ApplyEdit };
    ("textDocument/rangeFormatting") => { $crate::request::RangeFormatting };
    ("textDocument/onTypeFormatting") => { $crate::request::OnTypeFormatting };
    ("textDocument/formatting") => { $crate::request::Formatting };
    ("textDocument/rename") => { $crate::request::Rename };
    ("completionItem/resolve") => { $crate::request::ResolveCompletionItem };
    ("documentLink/resolve") => { $crate::request::DocumentLinkResolve };
    ("workspace/symbol") => { $crate::request::WorkspaceSymbol };
    ("workspace/executeCommand") => { $crate::request::ExecuteCommand }
}

/**

 The initialize request is sent as the first request from the client to the server.
 If the server receives request or notification before the `initialize` request it should act as follows:

 * for a request the respond should be errored with `code: -32001`. The message can be picked by the server.
 * notifications should be dropped.

*/
#[derive(Debug)]
pub enum Initialize {}

impl Request for Initialize {
    type Params = InitializeParams;
    type Result = InitializeResult;
    const METHOD: &'static str = "initialize";
}

/**
 * The shutdown request is sent from the client to the server. It asks the server to shut down,
 * but to not exit (otherwise the response might not be delivered correctly to the client).
 * There is a separate exit notification that asks the server to exit.
 */
#[derive(Debug)]
pub enum Shutdown {}

impl Request for Shutdown {
    type Params = ();
    type Result = ();
    const METHOD: &'static str = "shutdown";
}

/**
 * The show message request is sent from a server to a client to ask the client to display a particular message
 * in the user interface. In addition to the show message notification the request allows to pass actions and to
 * wait for an answer from the client.
 */
#[derive(Debug)]
pub enum ShowMessageRequest {}

impl Request for ShowMessageRequest {
    type Params = ShowMessageRequestParams;
    type Result = Option<MessageActionItem>;
    const METHOD: &'static str = "window/showMessageRequest";
}

/**
 The Completion request is sent from the client to the server to compute completion items at a given cursor position.
 Completion items are presented in the IntelliSense user interface. If computing full completion items is expensive,
 servers can additionally provide a handler for the completion item resolve request ('completionItem/resolve').
 This request is sent when a completion item is selected in the user interface. A typically use case is for example:
 the 'textDocument/completion' request doesn't fill in the documentation property for returned completion items
 since it is expensive to compute. When the item is selected in the user interface then a 'completionItem/resolve'
 request is sent with the selected completion item as a param. The returned completion item should have the
 documentation property filled in.
*/
#[derive(Debug)]
pub enum Completion {}

impl Request for Completion {
    type Params = CompletionParams;
    type Result = CompletionResponse;
    const METHOD: &'static str = "textDocument/completion";
}

/// The request is sent from the client to the server to resolve additional information for a given completion item.
#[derive(Debug)]
pub enum ResolveCompletionItem {}

impl Request for ResolveCompletionItem {
    type Params = CompletionItem;
    type Result = CompletionItem;
    const METHOD: &'static str = "completionItem/resolve";
}

/// The hover request is sent from the client to the server to request hover information at a given text
/// document position.
#[derive(Debug)]
pub enum HoverRequest {}

impl Request for HoverRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Hover>;
    const METHOD: &'static str = "textDocument/hover";
}

/// The signature help request is sent from the client to the server to request signature information at
/// a given cursor position.
#[derive(Debug)]
pub enum SignatureHelpRequest {}

impl Request for SignatureHelpRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<SignatureHelp>;
    const METHOD: &'static str = "textDocument/signatureHelp";
}

/// The goto definition request is sent from the client to the server to resolve the definition location of
/// a symbol at a given text document position.
#[derive(Debug)]
pub enum GotoDefinition {}

impl Request for GotoDefinition {
    type Params = TextDocumentPositionParams;
    type Result = Option<GotoDefinitionResponse>;
    const METHOD: &'static str = "textDocument/definition";
}

/**
 * GotoDefinition response can be single location or multiple ones.
 */
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GotoDefinitionResponse {
    Scalar(Location),
    Array(Vec<Location>),
}

/// The references request is sent from the client to the server to resolve project-wide references for the
/// symbol denoted by the given text document position.
pub enum References {}

impl Request for References {
    type Params = ReferenceParams;
    type Result = Option<Vec<Location>>;
    const METHOD: &'static str = "textDocument/references";
}

/**
 The document highlight request is sent from the client to the server to resolve a document highlights
 for a given text document position.
 For programming languages this usually highlights all references to the symbol scoped to this file.
 However we kept 'textDocument/documentHighlight' and 'textDocument/references' separate requests since
 the first one is allowed to be more fuzzy.
 Symbol matches usually have a DocumentHighlightKind of Read or Write whereas fuzzy or textual matches
 use Textas the kind.
*/
#[derive(Debug)]
pub enum DocumentHighlightRequest {}

impl Request for DocumentHighlightRequest {
    type Params = TextDocumentPositionParams;
    type Result = Option<Vec<DocumentHighlight>>;
    const METHOD: &'static str = "textDocument/documentHighlight";
}

/**
 * The document symbol request is sent from the client to the server to list all symbols found in a given
 * text document.
 */
#[derive(Debug)]
pub enum DocumentSymbol {}

impl Request for DocumentSymbol {
    type Params = DocumentSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "textDocument/documentSymbol";
}

/**
 * The workspace symbol request is sent from the client to the server to list project-wide symbols
 * matching the query string.
 */
#[derive(Debug)]
pub enum WorkspaceSymbol {}

impl Request for WorkspaceSymbol {
    type Params = WorkspaceSymbolParams;
    type Result = Option<Vec<SymbolInformation>>;
    const METHOD: &'static str = "workspace/symbol";
}

/// The workspace/executeCommand request is sent from the client to the server to trigger command execution on the server. In most cases the server creates a WorkspaceEdit structure and applies the changes to the workspace using the request workspace/applyEdit which is sent from the server to the client.
#[derive(Debug)]
pub enum ExecuteCommand {}

impl Request for ExecuteCommand {
    type Params = ExecuteCommandParams;
    type Result = Option<Value>;
    const METHOD: &'static str = "workspace/executeCommand";
}

/// The workspace/applyEdit request is sent from the server to the client to modify resource on the
/// client side.
#[derive(Debug)]
pub enum ApplyWorkspaceEdit {}

impl Request for ApplyWorkspaceEdit {
    type Params = ApplyWorkspaceEditParams;
    type Result = ApplyWorkspaceEditResponse;
    const METHOD: &'static str = "workspace/applyEdit";
}

/**
 * The code action request is sent from the client to the server to compute commands for a given text document
 * and range. The request is triggered when the user moves the cursor into a problem marker in the editor or
 * presses the lightbulb associated with a marker.
 */
#[derive(Debug)]
pub enum CodeActionRequest {}

impl Request for CodeActionRequest {
    type Params = CodeActionParams;
    type Result = Option<Vec<Command>>;
    const METHOD: &'static str = "textDocument/codeAction";
}

/**
 * The code lens request is sent from the client to the server to compute code lenses for a given text document.
 */
#[derive(Debug)]
pub enum CodeLensRequest {}

impl Request for CodeLensRequest {
    type Params = CodeLensParams;
    type Result = Option<Vec<CodeLens>>;
    const METHOD: &'static str = "textDocument/codeLens";
}

/**
 * The code lens resolve request is sent from the client to the server to resolve the command for a
 * given code lens item.
 */
#[derive(Debug)]
pub enum CodeLensResolve {}

impl Request for CodeLensResolve {
    type Params = CodeLens;
    type Result = CodeLens;
    const METHOD: &'static str = "codeLens/resolve";
}

/// The document links request is sent from the client to the server to request the location of links in a document.
#[derive(Debug)]
pub enum DocumentLinkRequest {}

impl Request for DocumentLinkRequest {
    type Params = DocumentLinkParams;
    type Result = Option<Vec<DocumentLink>>;
    const METHOD: &'static str = "textDocument/documentLink";
}

/**

 The document link resolve request is sent from the client to the server to resolve the target of
 a given document link.

*/
#[derive(Debug)]
pub enum DocumentLinkResolve {}

impl Request for DocumentLinkResolve {
    type Params = DocumentLink;
    type Result = DocumentLink;
    const METHOD: &'static str = "documentLink/resolve";
}

/**
 * The document formatting request is sent from the server to the client to format a whole document.
 */
#[derive(Debug)]
pub enum Formatting {}

impl Request for Formatting {
    type Params = DocumentFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/formatting";
}

/// The document range formatting request is sent from the client to the server to format a given range in a document.
#[derive(Debug)]
pub enum RangeFormatting {}

impl Request for RangeFormatting {
    type Params = DocumentRangeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/rangeFormatting";
}

/**
 * The document on type formatting request is sent from the client to the server to format parts of
 * the document during typing.
 */
#[derive(Debug)]
pub enum OnTypeFormatting {}

impl Request for OnTypeFormatting {
    type Params = DocumentOnTypeFormattingParams;
    type Result = Option<Vec<TextEdit>>;
    const METHOD: &'static str = "textDocument/onTypeFormatting";
}

/**
 * The rename request is sent from the client to the server to perform a workspace-wide rename of a symbol.
 */
#[derive(Debug)]
pub enum Rename {}

impl Request for Rename {
    type Params = RenameParams;
    type Result = Option<WorkspaceEdit>;
    const METHOD: &'static str = "textDocument/rename";
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io;
use std::ops::Drop;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};

use futures::{future, Future, BoxFuture, Sink};
use futures::sync::mpsc::Sender;
use tokio_core::reactor::Remote;
use tokio_service::Service;

use bytes::{BufMut, Bytes, BytesMut};

use admin;
use codec::Packet;
use job::Job;
use packet::PacketMagic;
use queues::{HandleJobStorage, JobQueuePriority, SharedJobStorage};
use worker::{SharedWorkers, Worker, Wake};
use constants::*;

fn new_res(ptype: u32, data: Bytes) -> Packet {
    Packet {
        magic: PacketMagic::RES,
        ptype: ptype,
        psize: data.len() as u32,
        data: data,
    }
}

fn new_noop() -> Packet {
    new_res(NOOP, Bytes::new())
}

type JobWaiters = Arc<Mutex<HashMap<Bytes, Vec<usize>>>>;
type SendersByConnId = Arc<Mutex<HashMap<usize, Sender<Packet>>>>;

pub struct GearmanService {
    pub conn_id: usize,
    pub queues: SharedJobStorage,
    pub workers: SharedWorkers,
    pub worker: Arc<Mutex<Worker>>,
    pub job_count: Arc<AtomicUsize>,
    senders_by_conn_id: SendersByConnId,
    job_waiters: JobWaiters,
    remote: Remote,
}

fn next_field(buf: &mut Bytes) -> Result<Bytes, io::Error> {
    match buf[..].iter().position(|b| *b == b'\0') {
        Some(null_pos) => {
            let value = buf.split_to(null_pos);
            buf.split_to(1);
            Ok(value)
        }
        None => Err(io::Error::new(io::ErrorKind::Other, "Can't find null")),
    }
}

impl Drop for GearmanService {
    fn drop(&mut self) {
        trace!("Dropping conn_id = {}", self.conn_id);
        self.workers.shutdown(self.conn_id);
        debug!("Dropped conn_id = {}", self.conn_id);
    }
}

impl GearmanService {
    /// Things that don't require a body should use this
    fn response_from_packet(&self, packet: &Packet) -> Packet {
        match packet.ptype {
            ADMIN_VERSION => {
                let resp_str = "OK some-rustygear-version\n";
                let mut resp_body = BytesMut::with_capacity(resp_str.len());
                resp_body.put(&resp_str[..]);
                Packet {
                    magic: PacketMagic::TEXT,
                    ptype: packet.ptype,
                    psize: resp_str.len() as u32,
                    data: resp_body.freeze(),
                }
            }
            ADMIN_STATUS => admin::admin_command_status(self.queues.clone(), self.workers.clone()),
            _ => {
                panic!("response_from_packet called with invalid ptype: {}",
                       packet.ptype)
            }
        }
    }

    fn send_to_conn_id(&self, conn_id: usize, packet: Packet) {
        let senders_by_conn_id = self.senders_by_conn_id.lock().unwrap();
        match senders_by_conn_id.get(&conn_id) {
            None => {
                panic!("No connection found for conn_id = {}", &conn_id); // XXX You can do better
            }
            Some(tx) => {
                let tx = tx.clone();
                self.remote.spawn(move |handle| {
                    handle.spawn(tx.send(packet).then(|res| {
                        match res {
                            Ok(_) => {}
                            Err(e) => error!("Send Error! {:?}", e),
                        }
                        Ok(())
                    }));
                    Ok(())
                });
            }
        }
    }

    pub fn new(conn_id: usize,
               queues: SharedJobStorage,
               workers: SharedWorkers,
               job_count: Arc<AtomicUsize>,
               senders_by_conn_id: SendersByConnId,
               job_waiters: JobWaiters,
               remote: Remote)
               -> GearmanService {
        GearmanService {
            conn_id: conn_id,
            queues: queues,
            worker: Arc::new(Mutex::new((Worker::new()))),
            workers: workers,
            job_count: job_count,
            senders_by_conn_id: senders_by_conn_id,
            job_waiters: job_waiters,
            remote: remote,
        }
    }

    fn no_response() -> Packet {
        Packet {
            magic: PacketMagic::TEXT,
            ptype: ADMIN_RESPONSE,
            psize: 0,
            data: Bytes::new(),
        }
    }

    fn handle_can_do(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        let workers = self.workers.clone();
        let conn_id = self.conn_id;
        debug!("CAN_DO fname = {:?}", packet.data);
        let mut worker = worker.lock().unwrap();
        worker.can_do(packet.data.clone());
        workers.clone().wakeup(&mut worker, conn_id);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_cant_do(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        debug!("CANT_DO fname = {:?}", packet.data);
        let mut worker = worker.lock().unwrap();
        worker.cant_do(&packet.data);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_grab_job_all(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(4 + j.handle.len() + j.fname.len() +
                                                       j.unique.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.unique);
                data.put_u8(b'\0');
                // reducer not implemented
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN_ALL, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_grab_job_uniq(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(3 + j.handle.len() + j.fname.len() +
                                                       j.unique.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.unique);
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN_UNIQ, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_grab_job(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(2 + j.handle.len() + j.fname.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_pre_sleep(&self) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        let ref mut w = worker.lock().unwrap();
        self.workers.clone().sleep(w, self.conn_id);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_submit_job(&self,
                         priority: JobQueuePriority,
                         wait: bool,
                         packet: Packet)
                         -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let conn_id = match wait {
            true => Some(self.conn_id),
            false => None,
        };
        let mut workers = self.workers.clone();
        let job_count = self.job_count.clone();
        let remote = self.remote.clone();
        let senders_by_conn_id = self.senders_by_conn_id.clone();
        let mut fields = packet.data.clone();
        trace!("fields = {:?}", fields);
        let fname = next_field(&mut fields).unwrap();
        let unique = next_field(&mut fields).unwrap();
        trace!("  --> fname = {:?} unique = {:?}", fname, unique);
        let mut add = false;
        let handle = match queues.coalesce_unique(&unique, conn_id) {
            Some(handle) => handle,
            None => {
                {
                    for wake in workers.queue_wake(&fname) {
                        let senders_by_conn_id = senders_by_conn_id.lock().unwrap();
                        match senders_by_conn_id.get(&wake) {
                            None => {
                                debug!("No connection found to wake up for conn_id = {}", wake);
                            }
                            Some(tx) => {
                                let tx = tx.clone();
                                remote.spawn(move |handle| {
                                    handle.spawn(tx.send(new_noop()).then(|res| {
                                        match res {
                                            Ok(_) => {}
                                            Err(e) => error!("Send Error! {:?}", e),
                                        }
                                        Ok(())
                                    }));
                                    Ok(())
                                });
                            }
                        }
                    }
                }
                // H:091234567890
                let mut handle = BytesMut::with_capacity(12);
                let job_num = job_count.fetch_add(1, Ordering::Relaxed);
                debug!("job_num = {}", job_num);
                handle.extend(format!("H:{:010}", job_num).as_bytes());
                add = true;
                handle.freeze()
            }
        };
        if add {
            let job = Arc::new(Job::new(fname, unique, fields, handle.clone()));
            info!("Created job {:?}", job);
            queues.add_job(job.clone(), priority, conn_id);
        }
        // If we don't store any senders, the sender will be dropped and the rx
        // stream should end thus releasing the waiter immediately.
        let psize = handle.len() as u32;
        if wait {
            // Fetch our sender
            let mut job_waiters = self.job_waiters.lock().unwrap();
            let mut waiters = job_waiters.entry(handle.clone()).or_insert(Vec::new());
            waiters.push(self.conn_id);
        }
        future::finished(Packet {
                magic: PacketMagic::RES,
                ptype: JOB_CREATED,
                psize: psize,
                data: handle,
            })
            .boxed()
    }

    fn handle_work_complete(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        // Search for handle
        let mut fields = packet.data.clone();
        let handle = next_field(&mut fields).unwrap();
        let worker = self.worker.clone();
        let queues = self.queues.clone();
        info!("Job is complete {:?}", handle);
        let mut worker = worker.lock().unwrap();
        match worker.get_assigned_job(&handle) {
            Some(ref mut j) => {
                let mut queues = queues.lock().unwrap();
                queues.remove_job(&j.unique);
            }
            None => {
                error!("WORK_COMPLETE received but no active jobs");
            }
        }
        worker.unassign_job(&handle);
        let mut job_waiters = self.job_waiters.lock().unwrap();
        // If there are waiters, send the packet to them
        if let Some(waiters) = job_waiters.remove(&handle) {
            for conn_id in waiters.iter() {
                self.send_to_conn_id(*conn_id, packet.clone());
            }
        }
        future::finished(Self::no_response()).boxed()
    }

    fn handle_work_update(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let mut fields = packet.data.clone();
        let handle = next_field(&mut fields).unwrap();
        let job_waiters = self.job_waiters.lock().unwrap();
        if let Some(waiters) = job_waiters.get(&handle) {
            for conn_id in waiters.iter() {
                self.send_to_conn_id(*conn_id, packet.clone());
            }
        }
        future::finished(Self::no_response()).boxed()
    }
}

impl Service for GearmanService {
    type Request = Packet;
    type Response = Packet;
    type Error = io::Error;
    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        debug!("Got a req {:?}", req);
        match req.ptype {
            ADMIN_VERSION | ADMIN_STATUS => future::ok(self.response_from_packet(&req)).boxed(),
            SUBMIT_JOB => self.handle_submit_job(PRIORITY_NORMAL, true, req),
            SUBMIT_JOB_HIGH => self.handle_submit_job(PRIORITY_HIGH, true, req),
            SUBMIT_JOB_LOW => self.handle_submit_job(PRIORITY_LOW, true, req),
            SUBMIT_JOB_BG => self.handle_submit_job(PRIORITY_NORMAL, false, req),
            SUBMIT_JOB_HIGH_BG => self.handle_submit_job(PRIORITY_HIGH, false, req),
            SUBMIT_JOB_LOW_BG => self.handle_submit_job(PRIORITY_LOW, false, req),
            PRE_SLEEP => self.handle_pre_sleep(),
            CAN_DO => self.handle_can_do(&req),
            CANT_DO => self.handle_cant_do(&req),
            GRAB_JOB => self.handle_grab_job(),
            GRAB_JOB_UNIQ => self.handle_grab_job_uniq(),
            GRAB_JOB_ALL => self.handle_grab_job_all(),
            WORK_COMPLETE => self.handle_work_complete(&req),
            WORK_STATUS | WORK_DATA | WORK_WARNING => self.handle_work_update(&req),/*
                    ECHO_REQ => self.handle_echo_req(&req),*/
            _ => {
                error!("Unimplemented: {:?} processing packet", req);
                future::err(io::Error::new(io::ErrorKind::Other,
                                           format!("Invalid packet type {}", req.ptype)))
                    .boxed()
            }
        }
    }
}
<commit_msg>fmt oops<commit_after>use std::collections::HashMap;
use std::io;
use std::ops::Drop;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering};

use futures::{future, Future, BoxFuture, Sink};
use futures::sync::mpsc::Sender;
use tokio_core::reactor::Remote;
use tokio_service::Service;

use bytes::{BufMut, Bytes, BytesMut};

use admin;
use codec::Packet;
use job::Job;
use packet::PacketMagic;
use queues::{HandleJobStorage, JobQueuePriority, SharedJobStorage};
use worker::{SharedWorkers, Worker, Wake};
use constants::*;

fn new_res(ptype: u32, data: Bytes) -> Packet {
    Packet {
        magic: PacketMagic::RES,
        ptype: ptype,
        psize: data.len() as u32,
        data: data,
    }
}

fn new_noop() -> Packet {
    new_res(NOOP, Bytes::new())
}

type JobWaiters = Arc<Mutex<HashMap<Bytes, Vec<usize>>>>;
type SendersByConnId = Arc<Mutex<HashMap<usize, Sender<Packet>>>>;

pub struct GearmanService {
    pub conn_id: usize,
    pub queues: SharedJobStorage,
    pub workers: SharedWorkers,
    pub worker: Arc<Mutex<Worker>>,
    pub job_count: Arc<AtomicUsize>,
    senders_by_conn_id: SendersByConnId,
    job_waiters: JobWaiters,
    remote: Remote,
}

fn next_field(buf: &mut Bytes) -> Result<Bytes, io::Error> {
    match buf[..].iter().position(|b| *b == b'\0') {
        Some(null_pos) => {
            let value = buf.split_to(null_pos);
            buf.split_to(1);
            Ok(value)
        }
        None => Err(io::Error::new(io::ErrorKind::Other, "Can't find null")),
    }
}

impl Drop for GearmanService {
    fn drop(&mut self) {
        trace!("Dropping conn_id = {}", self.conn_id);
        self.workers.shutdown(self.conn_id);
        debug!("Dropped conn_id = {}", self.conn_id);
    }
}

impl GearmanService {
    /// Things that don't require a body should use this
    fn response_from_packet(&self, packet: &Packet) -> Packet {
        match packet.ptype {
            ADMIN_VERSION => {
                let resp_str = "OK some-rustygear-version\n";
                let mut resp_body = BytesMut::with_capacity(resp_str.len());
                resp_body.put(&resp_str[..]);
                Packet {
                    magic: PacketMagic::TEXT,
                    ptype: packet.ptype,
                    psize: resp_str.len() as u32,
                    data: resp_body.freeze(),
                }
            }
            ADMIN_STATUS => admin::admin_command_status(self.queues.clone(), self.workers.clone()),
            _ => {
                panic!("response_from_packet called with invalid ptype: {}",
                       packet.ptype)
            }
        }
    }

    fn send_to_conn_id(&self, conn_id: usize, packet: Packet) {
        let senders_by_conn_id = self.senders_by_conn_id.lock().unwrap();
        match senders_by_conn_id.get(&conn_id) {
            None => {
                panic!("No connection found for conn_id = {}", &conn_id); // XXX You can do better
            }
            Some(tx) => {
                let tx = tx.clone();
                self.remote.spawn(move |handle| {
                    handle.spawn(tx.send(packet).then(|res| {
                        match res {
                            Ok(_) => {}
                            Err(e) => error!("Send Error! {:?}", e),
                        }
                        Ok(())
                    }));
                    Ok(())
                });
            }
        }
    }

    pub fn new(conn_id: usize,
               queues: SharedJobStorage,
               workers: SharedWorkers,
               job_count: Arc<AtomicUsize>,
               senders_by_conn_id: SendersByConnId,
               job_waiters: JobWaiters,
               remote: Remote)
               -> GearmanService {
        GearmanService {
            conn_id: conn_id,
            queues: queues,
            worker: Arc::new(Mutex::new((Worker::new()))),
            workers: workers,
            job_count: job_count,
            senders_by_conn_id: senders_by_conn_id,
            job_waiters: job_waiters,
            remote: remote,
        }
    }

    fn no_response() -> Packet {
        Packet {
            magic: PacketMagic::TEXT,
            ptype: ADMIN_RESPONSE,
            psize: 0,
            data: Bytes::new(),
        }
    }

    fn handle_can_do(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        let workers = self.workers.clone();
        let conn_id = self.conn_id;
        debug!("CAN_DO fname = {:?}", packet.data);
        let mut worker = worker.lock().unwrap();
        worker.can_do(packet.data.clone());
        workers.clone().wakeup(&mut worker, conn_id);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_cant_do(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        debug!("CANT_DO fname = {:?}", packet.data);
        let mut worker = worker.lock().unwrap();
        worker.cant_do(&packet.data);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_grab_job_all(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(4 + j.handle.len() + j.fname.len() +
                                                       j.unique.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.unique);
                data.put_u8(b'\0');
                // reducer not implemented
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN_ALL, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_grab_job_uniq(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(3 + j.handle.len() + j.fname.len() +
                                                       j.unique.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.unique);
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN_UNIQ, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_grab_job(&self) -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let worker = self.worker.clone();
        let mut worker = worker.lock().unwrap();
        let ref mut worker = worker;
        match queues.get_job(worker) {
            Some(ref j) => {
                let mut data = BytesMut::with_capacity(2 + j.handle.len() + j.fname.len() +
                                                       j.data.len());
                data.extend(&j.handle);
                data.put_u8(b'\0');
                data.extend(&j.fname);
                data.put_u8(b'\0');
                data.extend(&j.data);
                return future::finished(new_res(JOB_ASSIGN, data.freeze())).boxed();
            }
            None => {}
        };
        future::finished(new_res(NO_JOB, Bytes::new())).boxed()
    }

    fn handle_pre_sleep(&self) -> BoxFuture<Packet, io::Error> {
        let worker = self.worker.clone();
        let ref mut w = worker.lock().unwrap();
        self.workers.clone().sleep(w, self.conn_id);
        future::finished(Self::no_response()).boxed()
    }

    fn handle_submit_job(&self,
                         priority: JobQueuePriority,
                         wait: bool,
                         packet: Packet)
                         -> BoxFuture<Packet, io::Error> {
        let mut queues = self.queues.clone();
        let conn_id = match wait {
            true => Some(self.conn_id),
            false => None,
        };
        let mut workers = self.workers.clone();
        let job_count = self.job_count.clone();
        let remote = self.remote.clone();
        let senders_by_conn_id = self.senders_by_conn_id.clone();
        let mut fields = packet.data.clone();
        trace!("fields = {:?}", fields);
        let fname = next_field(&mut fields).unwrap();
        let unique = next_field(&mut fields).unwrap();
        trace!("  --> fname = {:?} unique = {:?}", fname, unique);
        let mut add = false;
        let handle = match queues.coalesce_unique(&unique, conn_id) {
            Some(handle) => handle,
            None => {
                {
                    for wake in workers.queue_wake(&fname) {
                        let senders_by_conn_id = senders_by_conn_id.lock().unwrap();
                        match senders_by_conn_id.get(&wake) {
                            None => {
                                debug!("No connection found to wake up for conn_id = {}", wake);
                            }
                            Some(tx) => {
                                let tx = tx.clone();
                                remote.spawn(move |handle| {
                                    handle.spawn(tx.send(new_noop()).then(|res| {
                                        match res {
                                            Ok(_) => {}
                                            Err(e) => error!("Send Error! {:?}", e),
                                        }
                                        Ok(())
                                    }));
                                    Ok(())
                                });
                            }
                        }
                    }
                }
                // H:091234567890
                let mut handle = BytesMut::with_capacity(12);
                let job_num = job_count.fetch_add(1, Ordering::Relaxed);
                debug!("job_num = {}", job_num);
                handle.extend(format!("H:{:010}", job_num).as_bytes());
                add = true;
                handle.freeze()
            }
        };
        if add {
            let job = Arc::new(Job::new(fname, unique, fields, handle.clone()));
            info!("Created job {:?}", job);
            queues.add_job(job.clone(), priority, conn_id);
        }
        // If we don't store any senders, the sender will be dropped and the rx
        // stream should end thus releasing the waiter immediately.
        let psize = handle.len() as u32;
        if wait {
            // Fetch our sender
            let mut job_waiters = self.job_waiters.lock().unwrap();
            let mut waiters = job_waiters.entry(handle.clone()).or_insert(Vec::new());
            waiters.push(self.conn_id);
        }
        future::finished(Packet {
                magic: PacketMagic::RES,
                ptype: JOB_CREATED,
                psize: psize,
                data: handle,
            })
            .boxed()
    }

    fn handle_work_complete(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        // Search for handle
        let mut fields = packet.data.clone();
        let handle = next_field(&mut fields).unwrap();
        let worker = self.worker.clone();
        let queues = self.queues.clone();
        info!("Job is complete {:?}", handle);
        let mut worker = worker.lock().unwrap();
        match worker.get_assigned_job(&handle) {
            Some(ref mut j) => {
                let mut queues = queues.lock().unwrap();
                queues.remove_job(&j.unique);
            }
            None => {
                error!("WORK_COMPLETE received but no active jobs");
            }
        }
        worker.unassign_job(&handle);
        let mut job_waiters = self.job_waiters.lock().unwrap();
        // If there are waiters, send the packet to them
        if let Some(waiters) = job_waiters.remove(&handle) {
            for conn_id in waiters.iter() {
                self.send_to_conn_id(*conn_id, packet.clone());
            }
        }
        future::finished(Self::no_response()).boxed()
    }

    fn handle_work_update(&self, packet: &Packet) -> BoxFuture<Packet, io::Error> {
        let mut fields = packet.data.clone();
        let handle = next_field(&mut fields).unwrap();
        let job_waiters = self.job_waiters.lock().unwrap();
        if let Some(waiters) = job_waiters.get(&handle) {
            for conn_id in waiters.iter() {
                self.send_to_conn_id(*conn_id, packet.clone());
            }
        }
        future::finished(Self::no_response()).boxed()
    }
}

impl Service for GearmanService {
    type Request = Packet;
    type Response = Packet;
    type Error = io::Error;
    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        debug!("Got a req {:?}", req);
        match req.ptype {
            ADMIN_VERSION | ADMIN_STATUS => future::ok(self.response_from_packet(&req)).boxed(),
            SUBMIT_JOB => self.handle_submit_job(PRIORITY_NORMAL, true, req),
            SUBMIT_JOB_HIGH => self.handle_submit_job(PRIORITY_HIGH, true, req),
            SUBMIT_JOB_LOW => self.handle_submit_job(PRIORITY_LOW, true, req),
            SUBMIT_JOB_BG => self.handle_submit_job(PRIORITY_NORMAL, false, req),
            SUBMIT_JOB_HIGH_BG => self.handle_submit_job(PRIORITY_HIGH, false, req),
            SUBMIT_JOB_LOW_BG => self.handle_submit_job(PRIORITY_LOW, false, req),
            PRE_SLEEP => self.handle_pre_sleep(),
            CAN_DO => self.handle_can_do(&req),
            CANT_DO => self.handle_cant_do(&req),
            GRAB_JOB => self.handle_grab_job(),
            GRAB_JOB_UNIQ => self.handle_grab_job_uniq(),
            GRAB_JOB_ALL => self.handle_grab_job_all(),
            WORK_COMPLETE => self.handle_work_complete(&req),
            WORK_STATUS | WORK_DATA | WORK_WARNING => self.handle_work_update(&req),/*
            ECHO_REQ => self.handle_echo_req(&req),*/
            _ => {
                error!("Unimplemented: {:?} processing packet", req);
                future::err(io::Error::new(io::ErrorKind::Other,
                                           format!("Invalid packet type {}", req.ptype)))
                    .boxed()
            }
        }
    }
}
<|endoftext|>


<commit_before>use reqwest;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    pub team: Vec<TeamConfig>,
    pub repo: Option<Vec<RepoConfig>>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/ghteam-auth.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<reqwest::Error> for CliError {
    fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
<commit_msg>Set default team/repo settings<commit_after>use reqwest;
use serde_json;
use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/ghteam-auth.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<reqwest::Error> for CliError {
    fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
<|endoftext|>