<commit_before>use std::collections::BTreeMap;

use rustc_serialize::json::Json;

use model::data::error::{LoadModelError, ModelError};
use super::SpriteDataSettings;

pub fn modify(settings: &mut SpriteDataSettings,
              delta: &BTreeMap<String, Json> ) -> Result<(), LoadModelError> {

  for (key, val) in delta.iter() {
    match key as &str {
      "sprite" => settings.sprite_name = try!(get_sprite_name(val)),

      "frame" => settings.frame_index = try!(get_frame_index(val)),

      "cut_from" => settings.cut_from =
                    try!(get_pair(val, "\"cut_from\"", "[u16, u16]", extract_u16)),

      "cut_offset" => settings.cut_offset =
                     try!(get_pair(val, "\"cut_offset\"", "[i8, i8]", extract_i8)),

      "size" => settings.size =
                        try!(get_pair(val, "\"size\"", "[u16, u16]", extract_u16)),

      "pin_to" => settings.pin_to =
                      try!(get_pair(val, "\"pin_to\"", "[i16, i16]", extract_i16)),

      "pin_offset" => settings.pin_offset =
                     try!(get_pair(val, "\"pin_offset\"", "[i8, i8]", extract_i8)),

      other => return try!(
                Err(ModelError::InvalidKey { key: other.to_string(),
                                             context: "\"defaults\" object" }))
    }
  }
  Ok(())
}

fn type_error(obj: &'static str, expected: &'static str) -> LoadModelError {
  LoadModelError::ModelError(ModelError::TypeError { obj: obj,
                                                     expected: expected })
}

fn get_sprite_name(value: &Json) -> Result<String, LoadModelError> {
  match value {
    &Json::String(ref s) => Ok(s.clone()),
    _ => Err(type_error("\"sprite\"", "string"))
  }
}

fn get_frame_index(value: &Json) -> Result<usize, LoadModelError> {
  let type_err = type_error("\"frame\"", "u64");
  match value {
    &Json::I64(n) => {
      if n < 0 {
        return Err(type_err);
      }
      Ok(n as usize)
    },
    &Json::U64(n) => Ok(n as usize),
    _ => return Err(type_err)
  }
}

fn get_pair<T, F>(value: &Json,
                  key: &'static str,
                  expected: &'static str,
                  extract: F ) -> Result<(T, T), LoadModelError>
  where F: Fn(&'static str, &'static str, &Json) -> Result<T, LoadModelError> {

  match value {
    &Json::Array(ref arr) => {
      if arr.len() != 2 {
        return Err(type_error(key, expected));
      }

      let a = try!(extract(key, expected, &arr[0]));
      let b = try!(extract(key, expected, &arr[1]));
      Ok((a, b))
    },
    _ => Err(type_error(key, expected)),
  }
}

// TODO: generalize these `extract...` methods

/// This is only to be used with `get_pair`
fn extract_i8(key: &'static str,
              expected: &'static str,
              obj: &Json ) -> Result<i8, LoadModelError> {

  match obj {
    // TODO check bounds
    &Json::I64(n) => Ok(n as i8),
    &Json::U64(n) => Ok(n as i8),
    _ => return Err(type_error(key, expected))
  }
}

/// This is only to be used with `get_pair`
fn extract_i16(key: &'static str,
               expected: &'static str,
               obj: &Json ) -> Result<i16, LoadModelError> {

  match obj {
    // TODO check bounds
    &Json::I64(n) => Ok(n as i16),
    &Json::U64(n) => Ok(n as i16),
    _ => return Err(type_error(key, expected))
  }
}

/// This is only to be used with `get_pair`
fn extract_u16(key: &'static str,
               expected: &'static str,
               obj: &Json ) -> Result<u16, LoadModelError> {

  match obj {
    &Json::I64(n) => {
      if n < 0 {
        return Err(type_error(key, expected));
      }
      // TODO check `n` < 2**16
      Ok(n as u16)
    },
    // TODO check `n` < 2**16
    &Json::U64(n) => Ok(n as u16),
    _ => return Err(type_error(key, expected))
  }
}
<commit_msg>Append to note<commit_after>use std::collections::BTreeMap;

use rustc_serialize::json::Json;

use model::data::error::{LoadModelError, ModelError};
use super::SpriteDataSettings;

pub fn modify(settings: &mut SpriteDataSettings,
              delta: &BTreeMap<String, Json> ) -> Result<(), LoadModelError> {

  for (key, val) in delta.iter() {
    match key as &str {
      "sprite" => settings.sprite_name = try!(get_sprite_name(val)),

      "frame" => settings.frame_index = try!(get_frame_index(val)),

      "cut_from" => settings.cut_from =
                    try!(get_pair(val, "\"cut_from\"", "[u16, u16]", extract_u16)),

      "cut_offset" => settings.cut_offset =
                     try!(get_pair(val, "\"cut_offset\"", "[i8, i8]", extract_i8)),

      "size" => settings.size =
                        try!(get_pair(val, "\"size\"", "[u16, u16]", extract_u16)),

      "pin_to" => settings.pin_to =
                      try!(get_pair(val, "\"pin_to\"", "[i16, i16]", extract_i16)),

      "pin_offset" => settings.pin_offset =
                     try!(get_pair(val, "\"pin_offset\"", "[i8, i8]", extract_i8)),

      other => return try!(
                Err(ModelError::InvalidKey { key: other.to_string(),
                                             context: "\"defaults\" object" }))
    }
  }
  Ok(())
}

fn type_error(obj: &'static str, expected: &'static str) -> LoadModelError {
  LoadModelError::ModelError(ModelError::TypeError { obj: obj,
                                                     expected: expected })
}

fn get_sprite_name(value: &Json) -> Result<String, LoadModelError> {
  match value {
    &Json::String(ref s) => Ok(s.clone()),
    _ => Err(type_error("\"sprite\"", "string"))
  }
}

fn get_frame_index(value: &Json) -> Result<usize, LoadModelError> {
  let type_err = type_error("\"frame\"", "u64");
  match value {
    &Json::I64(n) => {
      if n < 0 {
        return Err(type_err);
      }
      Ok(n as usize)
    },
    &Json::U64(n) => Ok(n as usize),
    _ => return Err(type_err)
  }
}

fn get_pair<T, F>(value: &Json,
                  key: &'static str,
                  expected: &'static str,
                  extract: F ) -> Result<(T, T), LoadModelError>
  where F: Fn(&'static str, &'static str, &Json) -> Result<T, LoadModelError> {

  match value {
    &Json::Array(ref arr) => {
      if arr.len() != 2 {
        return Err(type_error(key, expected));
      }

      let a = try!(extract(key, expected, &arr[0]));
      let b = try!(extract(key, expected, &arr[1]));
      Ok((a, b))
    },
    _ => Err(type_error(key, expected)),
  }
}

// TODO: generalize these `extract...` methods (use num crate)

/// This is only to be used with `get_pair`
fn extract_i8(key: &'static str,
              expected: &'static str,
              obj: &Json ) -> Result<i8, LoadModelError> {

  match obj {
    // TODO check bounds
    &Json::I64(n) => Ok(n as i8),
    &Json::U64(n) => Ok(n as i8),
    _ => return Err(type_error(key, expected))
  }
}

/// This is only to be used with `get_pair`
fn extract_i16(key: &'static str,
               expected: &'static str,
               obj: &Json ) -> Result<i16, LoadModelError> {

  match obj {
    // TODO check bounds
    &Json::I64(n) => Ok(n as i16),
    &Json::U64(n) => Ok(n as i16),
    _ => return Err(type_error(key, expected))
  }
}

/// This is only to be used with `get_pair`
fn extract_u16(key: &'static str,
               expected: &'static str,
               obj: &Json ) -> Result<u16, LoadModelError> {

  match obj {
    &Json::I64(n) => {
      if n < 0 {
        return Err(type_error(key, expected));
      }
      // TODO check `n` < 2**16
      Ok(n as u16)
    },
    // TODO check `n` < 2**16
    &Json::U64(n) => Ok(n as u16),
    _ => return Err(type_error(key, expected))
  }
}
<|endoftext|>


<commit_before>// Copyright 2018 The xi-editor Authors.
//
// 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.

//! Interactions with the file system.

use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use crate::tabs::BufferId;

#[cfg(feature = "notify")]
use crate::tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use crate::watcher::FileWatcher;
#[cfg(target_family = "unix")]
use std::{fs::Permissions, os::unix::fs::PermissionsExt};

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
    #[cfg(target_family = "unix")]
    pub permissions: Option<u32>,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            return Ok(Rope::from(""));
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8, self.get_info(id))
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
            #[cfg(target_family = "unix")]
            permissions: get_permissions(path),
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding, self.get_info(id))
                .map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo {
        encoding,
        mod_time: get_mod_time(&path),
        #[cfg(target_family = "unix")]
        permissions: get_permissions(&path),
        path: path.as_ref().to_owned(),
        has_changed: false,
    };
    Ok((rope, info))
}

#[allow(unused)]
fn try_save(
    path: &Path,
    text: &Rope,
    encoding: CharacterEncoding,
    file_info: Option<&FileInfo>,
) -> io::Result<()> {
    let tmp_extension = path.extension().map_or_else(
        || OsString::from("swp"),
        |ext| {
            let mut ext = ext.to_os_string();
            ext.push(".swp");
            ext
        },
    );
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    #[cfg(target_family = "unix")]
    {
        if let Some(info) = file_info {
            fs::set_permissions(path, Permissions::from_mode(info.permissions.unwrap_or(0o644)))?;
        }
    }

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P: AsRef<Path>>(path: P) -> Option<SystemTime> {
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

/// Returns the file permissions for the file at a given path on UNIXy systems,
/// if present.
#[cfg(target_family = "unix")]
fn get_permissions<P: AsRef<Path>>(path: P) -> Option<u32> {
    File::open(path).and_then(|f| f.metadata()).map(|meta| meta.permissions().mode()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<commit_msg>core-lib: don't error if file permissions can't be recovered in save<commit_after>// Copyright 2018 The xi-editor Authors.
//
// 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.

//! Interactions with the file system.

use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use crate::tabs::BufferId;

#[cfg(feature = "notify")]
use crate::tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use crate::watcher::FileWatcher;
#[cfg(target_family = "unix")]
use std::{fs::Permissions, os::unix::fs::PermissionsExt};

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
    #[cfg(target_family = "unix")]
    pub permissions: Option<u32>,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            return Ok(Rope::from(""));
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8, self.get_info(id))
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
            #[cfg(target_family = "unix")]
            permissions: get_permissions(path),
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding, self.get_info(id))
                .map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo {
        encoding,
        mod_time: get_mod_time(&path),
        #[cfg(target_family = "unix")]
        permissions: get_permissions(&path),
        path: path.as_ref().to_owned(),
        has_changed: false,
    };
    Ok((rope, info))
}

#[allow(unused)]
fn try_save(
    path: &Path,
    text: &Rope,
    encoding: CharacterEncoding,
    file_info: Option<&FileInfo>,
) -> io::Result<()> {
    let tmp_extension = path.extension().map_or_else(
        || OsString::from("swp"),
        |ext| {
            let mut ext = ext.to_os_string();
            ext.push(".swp");
            ext
        },
    );
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    #[cfg(target_family = "unix")]
    {
        if let Some(info) = file_info {
            fs::set_permissions(path, Permissions::from_mode(info.permissions.unwrap_or(0o644)))
                .unwrap_or_else(|e| {
                    warn!("Couldn't set permissions on file {} due to error {}", path.display(), e)
                });
        }
    }

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P: AsRef<Path>>(path: P) -> Option<SystemTime> {
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

/// Returns the file permissions for the file at a given path on UNIXy systems,
/// if present.
#[cfg(target_family = "unix")]
fn get_permissions<P: AsRef<Path>>(path: P) -> Option<u32> {
    File::open(path).and_then(|f| f.metadata()).map(|meta| meta.permissions().mode()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<|endoftext|>


<commit_before>use sass::op::Op;
use sass::number_value::NumberValue;

use std::fmt;
use std::borrow::Cow;

#[derive(Debug, Clone, PartialEq)]
pub enum ValuePart<'a> {
    Variable(Cow<'a, str>),
    String(Cow<'a, str>),
    Number(NumberValue<'a>),
    Operator(Op),
    List(Vec<ValuePart<'a>>),
}

impl<'a> ValuePart<'a> {
    pub fn concat_into_list(left: ValuePart<'a>, right: ValuePart<'a>) -> ValuePart<'static> {
        let list_parts = match (left, right) {
            (ValuePart::List(mut l), ValuePart::List(r)) => {
                l.extend(r);
                l
            },
            (l, ValuePart::List(r)) => {
                let mut ve = vec![l];
                ve.extend(r);
                ve
            },
            (ValuePart::List(mut l), r) => {
                l.push(r);
                l
            },
            (l, r) => {
                vec![l, r]
            },
        };
        ValuePart::List(list_parts).into_owned().into()
    }

    pub fn into_owned(self) -> ValuePart<'static> {
        match self {
            ValuePart::Variable(name) => ValuePart::Variable(name.into_owned().into()),
            ValuePart::String(str) => ValuePart::String(str.into_owned().into()),
            ValuePart::Number(nv) => ValuePart::Number(nv.into_owned().into()),
            ValuePart::List(v) => ValuePart::List(v.into_iter().map(|p| p.into_owned().into()).collect::<Vec<_>>()),
            ValuePart::Operator(o) => ValuePart::Operator(o),
        }
    }

    pub fn computed_number(&self) -> bool {
        match *self {
            ValuePart::Number(ref nv) => nv.computed,
            _ => false,
        }
    }
}

impl<'a> fmt::Display for ValuePart<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ValuePart::Variable(ref name) => write!(f, "{}", name),
            ValuePart::String(ref str) => write!(f, "{}", str),
            ValuePart::Number(ref num) => write!(f, "{}", num),
            ValuePart::List(ref list) => {
                write!(f, "{}", list.iter().map( |l| l.to_string() ).collect::<Vec<_>>().join(" "))
            },
            ValuePart::Operator(Op::Slash) => write!(f, "/"),
            ValuePart::Operator(Op::Comma) => write!(f, ","),
            ValuePart::Operator(..) => unreachable!(),
        }

    }
}<commit_msg>Fix spaces between list items<commit_after>use sass::op::Op;
use sass::number_value::NumberValue;

use std::fmt;
use std::borrow::Cow;

#[derive(Debug, Clone, PartialEq)]
pub enum ValuePart<'a> {
    Variable(Cow<'a, str>),
    String(Cow<'a, str>),
    Number(NumberValue<'a>),
    Operator(Op),
    List(Vec<ValuePart<'a>>),
}

impl<'a> ValuePart<'a> {
    pub fn concat_into_list(left: ValuePart<'a>, right: ValuePart<'a>) -> ValuePart<'static> {
        let list_parts = match (left, right) {
            (ValuePart::List(mut l), ValuePart::List(r)) => {
                l.extend(r);
                l
            },
            (l, ValuePart::List(r)) => {
                let mut ve = vec![l];
                ve.extend(r);
                ve
            },
            (ValuePart::List(mut l), r) => {
                l.push(r);
                l
            },
            (l, r) => {
                vec![l, r]
            },
        };
        ValuePart::List(list_parts).into_owned().into()
    }

    pub fn into_owned(self) -> ValuePart<'static> {
        match self {
            ValuePart::Variable(name) => ValuePart::Variable(name.into_owned().into()),
            ValuePart::String(str) => ValuePart::String(str.into_owned().into()),
            ValuePart::Number(nv) => ValuePart::Number(nv.into_owned().into()),
            ValuePart::List(v) => ValuePart::List(v.into_iter().map(|p| p.into_owned().into()).collect::<Vec<_>>()),
            ValuePart::Operator(o) => ValuePart::Operator(o),
        }
    }

    pub fn computed_number(&self) -> bool {
        match *self {
            ValuePart::Number(ref nv) => nv.computed,
            _ => false,
        }
    }
}

impl<'a> fmt::Display for ValuePart<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ValuePart::Variable(ref name) => write!(f, "{}", name),
            ValuePart::String(ref str) => write!(f, "{}", str),
            ValuePart::Number(ref num) => write!(f, "{}", num),
            ValuePart::List(ref list) => {
                let mut last_needs_space = false;
                let mut str = String::new();
                for item in list {
                    if last_needs_space &&
                       *item != ValuePart::Operator(Op::Slash) &&
                       *item != ValuePart::Operator(Op::Comma) {
                        str.push_str(" ");
                    }
                    str.push_str(&item.to_string());
                    match item {
                        &ValuePart::Operator(Op::Slash) => last_needs_space = false,
                        _ => last_needs_space = true,
                    }
                }
                write!(f, "{}", str)
            },
            ValuePart::Operator(Op::Slash) => write!(f, "/"),
            ValuePart::Operator(Op::Comma) => write!(f, ","),
            ValuePart::Operator(..) => unreachable!(),
        }

    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::number_value::NumberValue;
    use sass::op::Op;

    #[test]
    fn it_puts_spaces_between_list_parts() {
        let list = ValuePart::List(vec![
            ValuePart::Number(NumberValue::from_scalar(3.0)),
            ValuePart::Number(NumberValue::from_scalar(52.0)),
        ]);
        assert_eq!("3 52", list.to_string());
    }

    #[test]
    fn it_does_not_put_spaces_before_commas() {
        let list = ValuePart::List(vec![
            ValuePart::Number(NumberValue::from_scalar(3.0)),
            ValuePart::Operator(Op::Comma),
            ValuePart::Number(NumberValue::from_scalar(52.0)),
        ]);
        assert_eq!("3, 52", list.to_string());
    }

    #[test]
    fn it_does_not_put_spaces_around_slashes() {
        let list = ValuePart::List(vec![
            ValuePart::Number(NumberValue::from_scalar(3.0)),
            ValuePart::Operator(Op::Slash),
            ValuePart::Number(NumberValue::from_scalar(52.0)),
        ]);
        assert_eq!("3/52", list.to_string());
    }
}
<|endoftext|>


<commit_before>//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::Hash,
    iter::{FromIterator, IntoIterator},
    rc::Rc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. Once cached,
    /// we wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    // TODO: Why am I double-layering Arc's here?
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            Arc::ptr_eq(self.0.origin(), other.0.origin())
                && Arc::ptr_eq(self.0.source(), other.0.source())
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: crate::FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl crate::FileInterner, id: FileId) -> Arc<BasicFile> {
        let (name, contents) = crate::File::get_name_and_contents(db, id);
        Arc::new(BasicFile(SimpleFile::new(name.into(), contents.into())))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term {
        writer: termcolor::StandardStream,
    },
    #[allow(dead_code)]
    Test {
        errs: Vec<String>,
    },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    #[allow(dead_code)]
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    #[allow(dead_code)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    #[allow(dead_code)]
    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Diagnostics {
    val: Vec<Diagnostic>,
}
impl Diagnostics {
    pub fn new() -> Diagnostics {
        Diagnostics { val: vec![] }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        self.val.push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```ignore
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: self.val.iter().chain(second.val.iter()).cloned().collect(),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, mut other: Diagnostics) {
        self.val.append(&mut other.val);
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        for diag in self.val {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics { val: vec![err] }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        diags.val
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_parts(val: T, err: Diagnostics) -> Outcome<T> {
        Outcome { val, err }
    }

    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        RefOutcome {
            val: &self.val,
            err: &self.err,
        }
    }

    // Use this when you need to bypass Arc::as_ref.
    pub fn to_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        self.as_ref()
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }

    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Outcome<R> {
        Outcome {
            val: f(self.val),
            err: self.err,
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

pub struct RefOutcome<'a, T> {
    val: &'a T,
    err: &'a Diagnostics,
}

impl<'a, T> RefOutcome<'a, T> {
    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn val(self) -> Result<&'a T, &'a Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> &'a T {
        self.val
    }

    pub fn errs(self) -> &'a Diagnostics {
        self.err
    }

    pub fn split(self) -> (&'a T, &'a Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(&'a T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<commit_msg>Don't depend on forked codespan Eq, Hash impls<commit_after>//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::{Hash, Hasher},
    iter::{FromIterator, IntoIterator},
    rc::Rc,
    sync::Arc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. Once cached,
    /// we wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    // TODO: Why am I double-layering Arc's here?
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            Arc::ptr_eq(self.0.origin(), other.0.origin())
                && Arc::ptr_eq(self.0.source(), other.0.source())
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: crate::FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl crate::FileInterner, id: FileId) -> Arc<BasicFile> {
        let (name, contents) = crate::File::get_name_and_contents(db, id);
        Arc::new(BasicFile(SimpleFile::new(name.into(), contents.into())))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term {
        writer: termcolor::StandardStream,
    },
    #[allow(dead_code)]
    Test {
        errs: Vec<String>,
    },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    #[allow(dead_code)]
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    #[allow(dead_code)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    #[allow(dead_code)]
    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug)]
#[must_use]
pub struct Diagnostics {
    val: Arc<Vec<Diagnostic>>,
}
impl Diagnostics {
    pub fn new() -> Diagnostics {
        Diagnostics {
            val: Arc::new(vec![]),
        }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        Arc::make_mut(&mut self.val).push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```ignore
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: Arc::new(self.val.iter().chain(second.val.iter()).cloned().collect()),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, other: Diagnostics) {
        Arc::make_mut(&mut self.val).append(&mut other.into());
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let diags: Vec<_> = self.into();
        for diag in diags {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl PartialEq for Diagnostics {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.val, &other.val)
    }
}
impl Eq for Diagnostics {}
impl Hash for Diagnostics {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_usize(self.val.as_slice().as_ptr() as usize);
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics {
            val: Arc::new(vec![err]),
        }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        Arc::try_unwrap(diags.val).unwrap_or_else(|val| Vec::clone(&val))
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_parts(val: T, err: Diagnostics) -> Outcome<T> {
        Outcome { val, err }
    }

    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        RefOutcome {
            val: &self.val,
            err: &self.err,
        }
    }

    // Use this when you need to bypass Arc::as_ref.
    pub fn to_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        self.as_ref()
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }

    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Outcome<R> {
        Outcome {
            val: f(self.val),
            err: self.err,
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

pub struct RefOutcome<'a, T> {
    val: &'a T,
    err: &'a Diagnostics,
}

impl<'a, T> RefOutcome<'a, T> {
    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn val(self) -> Result<&'a T, &'a Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> &'a T {
        self.val
    }

    pub fn errs(self) -> &'a Diagnostics {
        self.err
    }

    pub fn split(self) -> (&'a T, &'a Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(&'a T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<|endoftext|>


<commit_before>// Version number tests
//
// These tests will make sure the version numbers in the readme and main crate documentation stay
// in sync with the crate version number

#![allow(non_fmt_panic)]

#[test]
fn test_readme_deps() {
    version_sync::assert_markdown_deps_updated!("../README.md");
}

#[test]
fn test_html_root_url() {
    version_sync::assert_html_root_url_updated!("src/lib.rs");
}
<commit_msg>Allows renamed lints<commit_after>// Version number tests
//
// These tests will make sure the version numbers in the readme and main crate documentation stay
// in sync with the crate version number

#![allow(renamed_and_removed_lints)]
#![allow(non_fmt_panic)]

#[test]
fn test_readme_deps() {
    version_sync::assert_markdown_deps_updated!("../README.md");
}

#[test]
fn test_html_root_url() {
    version_sync::assert_html_root_url_updated!("src/lib.rs");
}
<|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::PathBuf;
use std::env::{set_var, var};

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/clippy-driver"))
    }
}

fn run_mode(dir: &'static str, mode: &'static str) {
    let mut config = compiletest::Config::default();

    let cfg_mode = mode.parse().expect("Invalid mode");
    config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps -Dwarnings".to_owned());
    if let Ok(name) = var::<&str>("TESTNAME") {
        let s: String = name.to_owned();
        config.filter = Some(s)
    }

    config.mode = cfg_mode;
    config.build_base = PathBuf::from("target/debug/test_build_base")
        .canonicalize()
        .unwrap();
    config.src_base = PathBuf::from(format!("tests/{}", dir));
    config.rustc_path = clippy_driver_path();

    compiletest::run_tests(&config);
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    run_mode("run-pass", "run-pass");
    run_mode("ui", "ui");
}
<commit_msg>Build path from current_dir<commit_after>extern crate compiletest_rs as compiletest;

use std::path::PathBuf;
use std::env::{set_var, var};

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/clippy-driver"))
    }
}

fn run_mode(dir: &'static str, mode: &'static str) {
    let mut config = compiletest::Config::default();

    let cfg_mode = mode.parse().expect("Invalid mode");
    config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps -Dwarnings".to_owned());
    if let Ok(name) = var::<&str>("TESTNAME") {
        let s: String = name.to_owned();
        config.filter = Some(s)
    }

    config.mode = cfg_mode;
    config.build_base = {
        let mut path = std::env::current_dir().unwrap();
        path.push("target/debug/test_build_base");
        path
    };
    config.src_base = PathBuf::from(format!("tests/{}", dir));
    config.rustc_path = clippy_driver_path();

    compiletest::run_tests(&config);
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    run_mode("run-pass", "run-pass");
    run_mode("ui", "ui");
}
<|endoftext|>


<commit_before>#![feature(test)]

use compiletest_rs as compiletest;
extern crate test;

use std::env::{set_var, var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/clippy-driver"))
    }
}

fn host_libs() -> PathBuf {
    if let Some(path) = option_env!("HOST_LIBS") {
        PathBuf::from(path)
    } else {
        Path::new("target").join(env!("PROFILE"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn config(mode: &str, dir: PathBuf) -> compiletest::Config {
    let mut config = compiletest::Config::default();

    let cfg_mode = mode.parse().expect("Invalid mode");
    if let Ok(name) = var::<&str>("TESTNAME") {
        let s: String = name.to_owned();
        config.filter = Some(s)
    }

    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }

    // When we'll want to use `extern crate ..` for a dependency that is used
    // both by the crate and the compiler itself, we can't simply pass -L flags
    // as we'll get a duplicate matching versions. Instead, disambiguate with
    // `--extern dep=path`.
    // See https://github.com/rust-lang/rust-clippy/issues/4015.
    let needs_disambiguation = ["serde"];
    // This assumes that deps are compiled (they are for Cargo integration tests).
    let deps = std::fs::read_dir(host_libs().join("deps")).unwrap();
    let disambiguated = deps
        .filter_map(|dep| {
            let path = dep.ok()?.path();
            let name = path.file_name()?.to_string_lossy();
            // NOTE: This only handles a single dep
            // https://github.com/laumann/compiletest-rs/issues/101
            needs_disambiguation.iter().find_map(|dep| {
                if name.starts_with(&format!("lib{}-", dep)) {
                    Some(format!("--extern {}={}", dep, path.display()))
                } else {
                    None
                }
            })
        })
        .collect::<Vec<_>>();

    config.target_rustcflags = Some(format!(
        "-L {0} -L {0}/deps -Dwarnings -Zui-testing {1}",
        host_libs().display(),
        disambiguated.join(" ")
    ));

    config.mode = cfg_mode;
    config.build_base = if rustc_test_suite().is_some() {
        // we don't need access to the stderr files on travis
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        let mut path = std::env::current_dir().unwrap();
        path.push("target/debug/test_build_base");
        path
    };
    config.src_base = dir;
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(mode: &str, dir: PathBuf) {
    let cfg = config(mode, dir);
    // clean rmeta data, otherwise "cargo check; cargo test" fails (#2896)
    cfg.clean_rmeta();
    compiletest::run_tests(&cfg);
}

#[allow(clippy::identity_conversion)]
fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<test::TestDescAndFn>) -> Result<bool, io::Error> {
    let mut result = true;
    let opts = compiletest::test_opts(config);
    for dir in fs::read_dir(&config.src_base)? {
        let dir = dir?;
        if !dir.file_type()?.is_dir() {
            continue;
        }
        let dir_path = dir.path();
        set_var("CARGO_MANIFEST_DIR", &dir_path);
        for file in fs::read_dir(&dir_path)? {
            let file = file?;
            let file_path = file.path();
            if !file.file_type()?.is_file() {
                continue;
            }
            if file_path.extension() != Some(OsStr::new("rs")) {
                continue;
            }
            let paths = compiletest::common::TestPaths {
                file: file_path,
                base: config.src_base.clone(),
                relative_dir: dir_path.file_name().unwrap().into(),
            };
            let test_name = compiletest::make_test_name(&config, &*paths);
            let index = tests
                .iter()
                .position(|test| test.desc.name == test_name)
                .expect("The test should be in there");
            result &= test::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
        }
    }
    Ok(result)
}

fn run_ui_toml() {
    let path = PathBuf::from("tests/ui-toml").canonicalize().unwrap();
    let config = config("ui", path);
    let tests = compiletest::make_tests(&config);

    let res = run_ui_toml_tests(&config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            println!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    run_mode("ui", "tests/ui".into());
    run_ui_toml();
}
<commit_msg>Update for compiletest changes<commit_after>#![feature(test)]

use compiletest_rs as compiletest;
extern crate test;

use std::env::{set_var, var};
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

fn clippy_driver_path() -> PathBuf {
    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
        PathBuf::from(path)
    } else {
        PathBuf::from(concat!("target/", env!("PROFILE"), "/clippy-driver"))
    }
}

fn host_libs() -> PathBuf {
    if let Some(path) = option_env!("HOST_LIBS") {
        PathBuf::from(path)
    } else {
        Path::new("target").join(env!("PROFILE"))
    }
}

fn rustc_test_suite() -> Option<PathBuf> {
    option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}

fn rustc_lib_path() -> PathBuf {
    option_env!("RUSTC_LIB_PATH").unwrap().into()
}

fn config(mode: &str, dir: PathBuf) -> compiletest::Config {
    let mut config = compiletest::Config::default();

    let cfg_mode = mode.parse().expect("Invalid mode");
    if let Ok(name) = var::<&str>("TESTNAME") {
        let s: String = name.to_owned();
        config.filter = Some(s)
    }

    if rustc_test_suite().is_some() {
        config.run_lib_path = rustc_lib_path();
        config.compile_lib_path = rustc_lib_path();
    }

    // When we'll want to use `extern crate ..` for a dependency that is used
    // both by the crate and the compiler itself, we can't simply pass -L flags
    // as we'll get a duplicate matching versions. Instead, disambiguate with
    // `--extern dep=path`.
    // See https://github.com/rust-lang/rust-clippy/issues/4015.
    let needs_disambiguation = ["serde"];
    // This assumes that deps are compiled (they are for Cargo integration tests).
    let deps = std::fs::read_dir(host_libs().join("deps")).unwrap();
    let disambiguated = deps
        .filter_map(|dep| {
            let path = dep.ok()?.path();
            let name = path.file_name()?.to_string_lossy();
            // NOTE: This only handles a single dep
            // https://github.com/laumann/compiletest-rs/issues/101
            needs_disambiguation.iter().find_map(|dep| {
                if name.starts_with(&format!("lib{}-", dep)) {
                    Some(format!("--extern {}={}", dep, path.display()))
                } else {
                    None
                }
            })
        })
        .collect::<Vec<_>>();

    config.target_rustcflags = Some(format!(
        "-L {0} -L {0}/deps -Dwarnings -Zui-testing {1}",
        host_libs().display(),
        disambiguated.join(" ")
    ));

    config.mode = cfg_mode;
    config.build_base = if rustc_test_suite().is_some() {
        // we don't need access to the stderr files on travis
        let mut path = PathBuf::from(env!("OUT_DIR"));
        path.push("test_build_base");
        path
    } else {
        let mut path = std::env::current_dir().unwrap();
        path.push("target/debug/test_build_base");
        path
    };
    config.src_base = dir;
    config.rustc_path = clippy_driver_path();
    config
}

fn run_mode(mode: &str, dir: PathBuf) {
    let cfg = config(mode, dir);
    // clean rmeta data, otherwise "cargo check; cargo test" fails (#2896)
    cfg.clean_rmeta();
    compiletest::run_tests(&cfg);
}

#[allow(clippy::identity_conversion)]
fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<test::TestDescAndFn>) -> Result<bool, io::Error> {
    let mut result = true;
    let opts = compiletest::test_opts(config);
    for dir in fs::read_dir(&config.src_base)? {
        let dir = dir?;
        if !dir.file_type()?.is_dir() {
            continue;
        }
        let dir_path = dir.path();
        set_var("CARGO_MANIFEST_DIR", &dir_path);
        for file in fs::read_dir(&dir_path)? {
            let file = file?;
            let file_path = file.path();
            if !file.file_type()?.is_file() {
                continue;
            }
            if file_path.extension() != Some(OsStr::new("rs")) {
                continue;
            }
            let paths = compiletest::common::TestPaths {
                file: file_path,
                base: config.src_base.clone(),
                relative_dir: dir_path.file_name().unwrap().into(),
            };
            let test_name = compiletest::make_test_name(&config, &paths);
            let index = tests
                .iter()
                .position(|test| test.desc.name == test_name)
                .expect("The test should be in there");
            result &= test::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
        }
    }
    Ok(result)
}

fn run_ui_toml() {
    let path = PathBuf::from("tests/ui-toml").canonicalize().unwrap();
    let config = config("ui", path);
    let tests = compiletest::make_tests(&config);

    let res = run_ui_toml_tests(&config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            println!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("CLIPPY_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    run_mode("ui", "tests/ui".into());
    run_ui_toml();
}
<|endoftext|>


<commit_before>use std::time::Duration;
use std::{fmt, net};

use actix_net::server::ServerMessage;
use actix_net::service::{NewService, Service};
use futures::future::{err, ok, Either, FutureResult};
use futures::{Async, Future, Poll};
use tokio_reactor::Handle;
use tokio_tcp::TcpStream;
use tokio_timer::{sleep, Delay};

use super::channel::HttpProtocol;
use super::error::AcceptorError;
use super::handler::HttpHandler;
use super::settings::ServiceConfig;
use super::IoStream;

/// This trait indicates types that can create acceptor service for http server.
pub trait AcceptorServiceFactory: Send + Clone + 'static {
    type Io: IoStream + Send;
    type NewService: NewService<Request = TcpStream, Response = Self::Io>;

    fn create(&self) -> Self::NewService;
}

impl<F, T> AcceptorServiceFactory for F
where
    F: Fn() -> T + Send + Clone + 'static,
    T::Response: IoStream + Send,
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    type Io = T::Response;
    type NewService = T;

    fn create(&self) -> T {
        (self)()
    }
}

#[derive(Clone)]
/// Default acceptor service convert `TcpStream` to a `tokio_tcp::TcpStream`
pub(crate) struct DefaultAcceptor;

impl AcceptorServiceFactory for DefaultAcceptor {
    type Io = TcpStream;
    type NewService = DefaultAcceptor;

    fn create(&self) -> Self::NewService {
        DefaultAcceptor
    }
}

impl NewService for DefaultAcceptor {
    type Request = TcpStream;
    type Response = TcpStream;
    type Error = ();
    type InitError = ();
    type Service = DefaultAcceptor;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(DefaultAcceptor)
    }
}

impl Service for DefaultAcceptor {
    type Request = TcpStream;
    type Response = TcpStream;
    type Error = ();
    type Future = FutureResult<Self::Response, Self::Error>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        ok(req)
    }
}

pub(crate) struct TcpAcceptor<T> {
    inner: T,
}

impl<T, E> TcpAcceptor<T>
where
    T: NewService<Request = TcpStream, Error = AcceptorError<E>>,
    T::InitError: fmt::Debug,
{
    pub(crate) fn new(inner: T) -> Self {
        TcpAcceptor { inner }
    }
}

impl<T, E> NewService for TcpAcceptor<T>
where
    T: NewService<Request = TcpStream, Error = AcceptorError<E>>,
    T::InitError: fmt::Debug,
{
    type Request = net::TcpStream;
    type Response = T::Response;
    type Error = AcceptorError<E>;
    type InitError = T::InitError;
    type Service = TcpAcceptorService<T::Service>;
    type Future = TcpAcceptorResponse<T>;

    fn new_service(&self) -> Self::Future {
        TcpAcceptorResponse {
            fut: self.inner.new_service(),
        }
    }
}

pub(crate) struct TcpAcceptorResponse<T>
where
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    fut: T::Future,
}

impl<T> Future for TcpAcceptorResponse<T>
where
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    type Item = TcpAcceptorService<T::Service>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll() {
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Ok(Async::Ready(service)) => {
                Ok(Async::Ready(TcpAcceptorService { inner: service }))
            }
            Err(e) => {
                error!("Can not create accetor service: {:?}", e);
                Err(e)
            }
        }
    }
}

pub(crate) struct TcpAcceptorService<T> {
    inner: T,
}

impl<T, E> Service for TcpAcceptorService<T>
where
    T: Service<Request = TcpStream, Error = AcceptorError<E>>,
{
    type Request = net::TcpStream;
    type Response = T::Response;
    type Error = AcceptorError<E>;
    type Future = Either<T::Future, FutureResult<Self::Response, Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready()
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        let stream = TcpStream::from_std(req, &Handle::default()).map_err(|e| {
            error!("Can not convert to an async tcp stream: {}", e);
            AcceptorError::Io(e)
        });

        match stream {
            Ok(stream) => Either::A(self.inner.call(stream)),
            Err(e) => Either::B(err(e)),
        }
    }
}

#[doc(hidden)]
/// Acceptor timeout middleware
///
/// Applies timeout to request prcoessing.
pub struct AcceptorTimeout<T> {
    inner: T,
    timeout: Duration,
}

impl<T: NewService> AcceptorTimeout<T> {
    pub(crate) fn new(timeout: u64, inner: T) -> Self {
        Self {
            inner,
            timeout: Duration::from_millis(timeout),
        }
    }
}

impl<T: NewService> NewService for AcceptorTimeout<T> {
    type Request = T::Request;
    type Response = T::Response;
    type Error = AcceptorError<T::Error>;
    type InitError = T::InitError;
    type Service = AcceptorTimeoutService<T::Service>;
    type Future = AcceptorTimeoutFut<T>;

    fn new_service(&self) -> Self::Future {
        AcceptorTimeoutFut {
            fut: self.inner.new_service(),
            timeout: self.timeout,
        }
    }
}

#[doc(hidden)]
pub struct AcceptorTimeoutFut<T: NewService> {
    fut: T::Future,
    timeout: Duration,
}

impl<T: NewService> Future for AcceptorTimeoutFut<T> {
    type Item = AcceptorTimeoutService<T::Service>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let inner = try_ready!(self.fut.poll());
        Ok(Async::Ready(AcceptorTimeoutService {
            inner,
            timeout: self.timeout,
        }))
    }
}

#[doc(hidden)]
/// Acceptor timeout service
///
/// Applies timeout to request prcoessing.
pub struct AcceptorTimeoutService<T> {
    inner: T,
    timeout: Duration,
}

impl<T: Service> Service for AcceptorTimeoutService<T> {
    type Request = T::Request;
    type Response = T::Response;
    type Error = AcceptorError<T::Error>;
    type Future = AcceptorTimeoutResponse<T>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready().map_err(AcceptorError::Service)
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        AcceptorTimeoutResponse {
            fut: self.inner.call(req),
            sleep: sleep(self.timeout),
        }
    }
}

#[doc(hidden)]
pub struct AcceptorTimeoutResponse<T: Service> {
    fut: T::Future,
    sleep: Delay,
}

impl<T: Service> Future for AcceptorTimeoutResponse<T> {
    type Item = T::Response;
    type Error = AcceptorError<T::Error>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll().map_err(AcceptorError::Service)? {
            Async::NotReady => match self.sleep.poll() {
                Err(_) => Err(AcceptorError::Timeout),
                Ok(Async::Ready(_)) => Err(AcceptorError::Timeout),
                Ok(Async::NotReady) => Ok(Async::NotReady),
            },
            Async::Ready(resp) => Ok(Async::Ready(resp)),
        }
    }
}

pub(crate) struct ServerMessageAcceptor<T, H: HttpHandler> {
    inner: T,
    settings: ServiceConfig<H>,
}

impl<T, H> ServerMessageAcceptor<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    pub(crate) fn new(settings: ServiceConfig<H>, inner: T) -> Self {
        ServerMessageAcceptor { inner, settings }
    }
}

impl<T, H> NewService for ServerMessageAcceptor<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    type Request = ServerMessage;
    type Response = ();
    type Error = T::Error;
    type InitError = T::InitError;
    type Service = ServerMessageAcceptorService<T::Service, H>;
    type Future = ServerMessageAcceptorResponse<T, H>;

    fn new_service(&self) -> Self::Future {
        ServerMessageAcceptorResponse {
            fut: self.inner.new_service(),
            settings: self.settings.clone(),
        }
    }
}

pub(crate) struct ServerMessageAcceptorResponse<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    fut: T::Future,
    settings: ServiceConfig<H>,
}

impl<T, H> Future for ServerMessageAcceptorResponse<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    type Item = ServerMessageAcceptorService<T::Service, H>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll()? {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(service) => Ok(Async::Ready(ServerMessageAcceptorService {
                inner: service,
                settings: self.settings.clone(),
            })),
        }
    }
}

pub(crate) struct ServerMessageAcceptorService<T, H: HttpHandler> {
    inner: T,
    settings: ServiceConfig<H>,
}

impl<T, H> Service for ServerMessageAcceptorService<T, H>
where
    H: HttpHandler,
    T: Service<Request = net::TcpStream>,
{
    type Request = ServerMessage;
    type Response = ();
    type Error = T::Error;
    type Future =
        Either<ServerMessageAcceptorServiceFut<T>, FutureResult<(), Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready()
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        match req {
            ServerMessage::Connect(stream) => {
                Either::A(ServerMessageAcceptorServiceFut {
                    fut: self.inner.call(stream),
                })
            }
            ServerMessage::Shutdown(_) => Either::B(ok(())),
            ServerMessage::ForceShutdown => {
                self.settings
                    .head()
                    .traverse(|proto: &mut HttpProtocol<TcpStream, H>| proto.shutdown());
                Either::B(ok(()))
            }
        }
    }
}

pub(crate) struct ServerMessageAcceptorServiceFut<T: Service> {
    fut: T::Future,
}

impl<T> Future for ServerMessageAcceptorServiceFut<T>
where
    T: Service,
{
    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll()? {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(_) => Ok(Async::Ready(())),
        }
    }
}
<commit_msg>make AcceptorTimeout::new public<commit_after>use std::time::Duration;
use std::{fmt, net};

use actix_net::server::ServerMessage;
use actix_net::service::{NewService, Service};
use futures::future::{err, ok, Either, FutureResult};
use futures::{Async, Future, Poll};
use tokio_reactor::Handle;
use tokio_tcp::TcpStream;
use tokio_timer::{sleep, Delay};

use super::channel::HttpProtocol;
use super::error::AcceptorError;
use super::handler::HttpHandler;
use super::settings::ServiceConfig;
use super::IoStream;

/// This trait indicates types that can create acceptor service for http server.
pub trait AcceptorServiceFactory: Send + Clone + 'static {
    type Io: IoStream + Send;
    type NewService: NewService<Request = TcpStream, Response = Self::Io>;

    fn create(&self) -> Self::NewService;
}

impl<F, T> AcceptorServiceFactory for F
where
    F: Fn() -> T + Send + Clone + 'static,
    T::Response: IoStream + Send,
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    type Io = T::Response;
    type NewService = T;

    fn create(&self) -> T {
        (self)()
    }
}

#[derive(Clone)]
/// Default acceptor service convert `TcpStream` to a `tokio_tcp::TcpStream`
pub(crate) struct DefaultAcceptor;

impl AcceptorServiceFactory for DefaultAcceptor {
    type Io = TcpStream;
    type NewService = DefaultAcceptor;

    fn create(&self) -> Self::NewService {
        DefaultAcceptor
    }
}

impl NewService for DefaultAcceptor {
    type Request = TcpStream;
    type Response = TcpStream;
    type Error = ();
    type InitError = ();
    type Service = DefaultAcceptor;
    type Future = FutureResult<Self::Service, Self::InitError>;

    fn new_service(&self) -> Self::Future {
        ok(DefaultAcceptor)
    }
}

impl Service for DefaultAcceptor {
    type Request = TcpStream;
    type Response = TcpStream;
    type Error = ();
    type Future = FutureResult<Self::Response, Self::Error>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        Ok(Async::Ready(()))
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        ok(req)
    }
}

pub(crate) struct TcpAcceptor<T> {
    inner: T,
}

impl<T, E> TcpAcceptor<T>
where
    T: NewService<Request = TcpStream, Error = AcceptorError<E>>,
    T::InitError: fmt::Debug,
{
    pub(crate) fn new(inner: T) -> Self {
        TcpAcceptor { inner }
    }
}

impl<T, E> NewService for TcpAcceptor<T>
where
    T: NewService<Request = TcpStream, Error = AcceptorError<E>>,
    T::InitError: fmt::Debug,
{
    type Request = net::TcpStream;
    type Response = T::Response;
    type Error = AcceptorError<E>;
    type InitError = T::InitError;
    type Service = TcpAcceptorService<T::Service>;
    type Future = TcpAcceptorResponse<T>;

    fn new_service(&self) -> Self::Future {
        TcpAcceptorResponse {
            fut: self.inner.new_service(),
        }
    }
}

pub(crate) struct TcpAcceptorResponse<T>
where
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    fut: T::Future,
}

impl<T> Future for TcpAcceptorResponse<T>
where
    T: NewService<Request = TcpStream>,
    T::InitError: fmt::Debug,
{
    type Item = TcpAcceptorService<T::Service>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll() {
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Ok(Async::Ready(service)) => {
                Ok(Async::Ready(TcpAcceptorService { inner: service }))
            }
            Err(e) => {
                error!("Can not create accetor service: {:?}", e);
                Err(e)
            }
        }
    }
}

pub(crate) struct TcpAcceptorService<T> {
    inner: T,
}

impl<T, E> Service for TcpAcceptorService<T>
where
    T: Service<Request = TcpStream, Error = AcceptorError<E>>,
{
    type Request = net::TcpStream;
    type Response = T::Response;
    type Error = AcceptorError<E>;
    type Future = Either<T::Future, FutureResult<Self::Response, Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready()
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        let stream = TcpStream::from_std(req, &Handle::default()).map_err(|e| {
            error!("Can not convert to an async tcp stream: {}", e);
            AcceptorError::Io(e)
        });

        match stream {
            Ok(stream) => Either::A(self.inner.call(stream)),
            Err(e) => Either::B(err(e)),
        }
    }
}

#[doc(hidden)]
/// Acceptor timeout middleware
///
/// Applies timeout to request prcoessing.
pub struct AcceptorTimeout<T> {
    inner: T,
    timeout: Duration,
}

impl<T: NewService> AcceptorTimeout<T> {
    /// Create new `AcceptorTimeout` instance. timeout is in milliseconds.
    pub fn new(timeout: u64, inner: T) -> Self {
        Self {
            inner,
            timeout: Duration::from_millis(timeout),
        }
    }
}

impl<T: NewService> NewService for AcceptorTimeout<T> {
    type Request = T::Request;
    type Response = T::Response;
    type Error = AcceptorError<T::Error>;
    type InitError = T::InitError;
    type Service = AcceptorTimeoutService<T::Service>;
    type Future = AcceptorTimeoutFut<T>;

    fn new_service(&self) -> Self::Future {
        AcceptorTimeoutFut {
            fut: self.inner.new_service(),
            timeout: self.timeout,
        }
    }
}

#[doc(hidden)]
pub struct AcceptorTimeoutFut<T: NewService> {
    fut: T::Future,
    timeout: Duration,
}

impl<T: NewService> Future for AcceptorTimeoutFut<T> {
    type Item = AcceptorTimeoutService<T::Service>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let inner = try_ready!(self.fut.poll());
        Ok(Async::Ready(AcceptorTimeoutService {
            inner,
            timeout: self.timeout,
        }))
    }
}

#[doc(hidden)]
/// Acceptor timeout service
///
/// Applies timeout to request prcoessing.
pub struct AcceptorTimeoutService<T> {
    inner: T,
    timeout: Duration,
}

impl<T: Service> Service for AcceptorTimeoutService<T> {
    type Request = T::Request;
    type Response = T::Response;
    type Error = AcceptorError<T::Error>;
    type Future = AcceptorTimeoutResponse<T>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready().map_err(AcceptorError::Service)
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        AcceptorTimeoutResponse {
            fut: self.inner.call(req),
            sleep: sleep(self.timeout),
        }
    }
}

#[doc(hidden)]
pub struct AcceptorTimeoutResponse<T: Service> {
    fut: T::Future,
    sleep: Delay,
}

impl<T: Service> Future for AcceptorTimeoutResponse<T> {
    type Item = T::Response;
    type Error = AcceptorError<T::Error>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll().map_err(AcceptorError::Service)? {
            Async::NotReady => match self.sleep.poll() {
                Err(_) => Err(AcceptorError::Timeout),
                Ok(Async::Ready(_)) => Err(AcceptorError::Timeout),
                Ok(Async::NotReady) => Ok(Async::NotReady),
            },
            Async::Ready(resp) => Ok(Async::Ready(resp)),
        }
    }
}

pub(crate) struct ServerMessageAcceptor<T, H: HttpHandler> {
    inner: T,
    settings: ServiceConfig<H>,
}

impl<T, H> ServerMessageAcceptor<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    pub(crate) fn new(settings: ServiceConfig<H>, inner: T) -> Self {
        ServerMessageAcceptor { inner, settings }
    }
}

impl<T, H> NewService for ServerMessageAcceptor<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    type Request = ServerMessage;
    type Response = ();
    type Error = T::Error;
    type InitError = T::InitError;
    type Service = ServerMessageAcceptorService<T::Service, H>;
    type Future = ServerMessageAcceptorResponse<T, H>;

    fn new_service(&self) -> Self::Future {
        ServerMessageAcceptorResponse {
            fut: self.inner.new_service(),
            settings: self.settings.clone(),
        }
    }
}

pub(crate) struct ServerMessageAcceptorResponse<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    fut: T::Future,
    settings: ServiceConfig<H>,
}

impl<T, H> Future for ServerMessageAcceptorResponse<T, H>
where
    H: HttpHandler,
    T: NewService<Request = net::TcpStream>,
{
    type Item = ServerMessageAcceptorService<T::Service, H>;
    type Error = T::InitError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll()? {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(service) => Ok(Async::Ready(ServerMessageAcceptorService {
                inner: service,
                settings: self.settings.clone(),
            })),
        }
    }
}

pub(crate) struct ServerMessageAcceptorService<T, H: HttpHandler> {
    inner: T,
    settings: ServiceConfig<H>,
}

impl<T, H> Service for ServerMessageAcceptorService<T, H>
where
    H: HttpHandler,
    T: Service<Request = net::TcpStream>,
{
    type Request = ServerMessage;
    type Response = ();
    type Error = T::Error;
    type Future =
        Either<ServerMessageAcceptorServiceFut<T>, FutureResult<(), Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll_ready()
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        match req {
            ServerMessage::Connect(stream) => {
                Either::A(ServerMessageAcceptorServiceFut {
                    fut: self.inner.call(stream),
                })
            }
            ServerMessage::Shutdown(_) => Either::B(ok(())),
            ServerMessage::ForceShutdown => {
                self.settings
                    .head()
                    .traverse(|proto: &mut HttpProtocol<TcpStream, H>| proto.shutdown());
                Either::B(ok(()))
            }
        }
    }
}

pub(crate) struct ServerMessageAcceptorServiceFut<T: Service> {
    fut: T::Future,
}

impl<T> Future for ServerMessageAcceptorServiceFut<T>
where
    T: Service,
{
    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.fut.poll()? {
            Async::NotReady => Ok(Async::NotReady),
            Async::Ready(_) => Ok(Async::Ready(())),
        }
    }
}
<|endoftext|>