<commit_before>pub fn parse_line(line: &str) -> Vec<String> {
    let mut vec = Vec::with_capacity(32);
    let mut tmp_str = String::with_capacity(line.len());
    let mut in_quotes = false;

    for c in line.chars() {
        match c {
            ' ' => {
                if in_quotes {
                    tmp_str.push(' ');
                    continue;
                }
                if !tmp_str.is_empty() {
                    vec.push(tmp_str);
                }
                tmp_str = String::with_capacity(line.len());
            }
            '"' => {
                in_quotes = !in_quotes;
            }
            _ => {
                tmp_str.push(c);
            }
        }
    }

    if !tmp_str.is_empty() {
        vec.push(tmp_str);
    }
    vec
}

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

    #[test]
    fn test_parse_single_arg() {
        let args = parse_line("abc");
        assert!(args.len() == 1);
        assert_eq!(args[0], "abc");

        let args = parse_line("abc ");
        assert!(args.len() == 1);
        assert_eq!(args[0], "abc");

        let args = parse_line("abc   ");
        assert!(args.len() == 1);
        assert_eq!(args[0], "abc");
    }

    #[test]
    fn test_parse_two_args() {
        let args = parse_line("abc 123");
        assert!(args.len() == 2);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");

        let args = parse_line("abc  123");
        assert!(args.len() == 2);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
    }

    #[test]
    fn test_parse_multiargs() {
        let args = parse_line("abc  123    def");
        assert!(args.len() == 3);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
        assert_eq!(args[2], "def");

        let args = parse_line("abc 123    def 456");
        assert!(args.len() == 4);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
        assert_eq!(args[2], "def");
        assert_eq!(args[3], "456");
    }

    #[test]
    fn test_parse_quotes() {
        let args = parse_line("\"abc  123\"    def");
        assert!(args.len() == 2);
        assert_eq!(args[0], "abc  123");
        assert_eq!(args[1], "def");

        let args = parse_line("abc\"  \"123    def");
        assert!(args.len() == 2);
        assert_eq!(args[0], "abc  123");
        assert_eq!(args[1], "def");

        let args = parse_line("\"  \"123 def");
        assert!(args.len() == 2);
        assert_eq!(args[0], "  123");
        assert_eq!(args[1], "def");

        let args = parse_line("\"  abc");
        assert!(args.len() == 1);
        assert_eq!(args[0], "  abc");
    }
}
<commit_msg>Use assert_eq in wallet parser tests<commit_after>pub fn parse_line(line: &str) -> Vec<String> {
    let mut vec = Vec::with_capacity(32);
    let mut tmp_str = String::with_capacity(line.len());
    let mut in_quotes = false;

    for c in line.chars() {
        match c {
            ' ' => {
                if in_quotes {
                    tmp_str.push(' ');
                    continue;
                }
                if !tmp_str.is_empty() {
                    vec.push(tmp_str);
                }
                tmp_str = String::with_capacity(line.len());
            }
            '"' => {
                in_quotes = !in_quotes;
            }
            _ => {
                tmp_str.push(c);
            }
        }
    }

    if !tmp_str.is_empty() {
        vec.push(tmp_str);
    }
    vec
}

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

    #[test]
    fn test_parse_single_arg() {
        let args = parse_line("abc");
        assert_eq!(args.len(), 1);
        assert_eq!(args[0], "abc");

        let args = parse_line("abc ");
        assert_eq!(args.len(), 1);
        assert_eq!(args[0], "abc");

        let args = parse_line("abc   ");
        assert_eq!(args.len(), 1);
        assert_eq!(args[0], "abc");
    }

    #[test]
    fn test_parse_two_args() {
        let args = parse_line("abc 123");
        assert_eq!(args.len(), 2);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");

        let args = parse_line("abc  123");
        assert_eq!(args.len(), 2);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
    }

    #[test]
    fn test_parse_multiargs() {
        let args = parse_line("abc  123    def");
        assert_eq!(args.len(), 3);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
        assert_eq!(args[2], "def");

        let args = parse_line("abc 123    def 456");
        assert_eq!(args.len(), 4);
        assert_eq!(args[0], "abc");
        assert_eq!(args[1], "123");
        assert_eq!(args[2], "def");
        assert_eq!(args[3], "456");
    }

    #[test]
    fn test_parse_quotes() {
        let args = parse_line("\"abc  123\"    def");
        assert_eq!(args.len(), 2);
        assert_eq!(args[0], "abc  123");
        assert_eq!(args[1], "def");

        let args = parse_line("abc\"  \"123    def");
        assert_eq!(args.len(), 2);
        assert_eq!(args[0], "abc  123");
        assert_eq!(args[1], "def");

        let args = parse_line("\"  \"123 def");
        assert_eq!(args.len(), 2);
        assert_eq!(args[0], "  123");
        assert_eq!(args[1], "def");

        let args = parse_line("\"  abc");
        assert_eq!(args.len(), 1);
        assert_eq!(args[0], "  abc");
    }
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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.

//! Trivial impls for `std` types.

use core::mem;
use core::mem::MaybeUninit;
use core::pin::Pin;

use crate::move_ref::MoveRef;
use crate::new::CopyNew;
use crate::new::MoveNew;
use crate::new::Swap;

macro_rules! trivial_move {
  ($($ty:ty $(where [$($targs:tt)*])?),* $(,)?) => {$(
    unsafe impl<$($($targs)*)?> MoveNew for $ty {
      unsafe fn move_new(
        src: Pin<MoveRef<Self>>,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let src = Pin::into_inner_unchecked(src);
        let this = Pin::into_inner_unchecked(this);
        this.write(MoveRef::into_inner(src));
      }
    }

    impl<$($($targs)*)?> Swap for $ty {
      fn swap_with(self: Pin<&mut Self>, that: Pin<&mut Self>) {
        unsafe {
          let zelf = Pin::into_inner_unchecked(self);
          let that = Pin::into_inner_unchecked(that);
          mem::swap(zelf, that);
        }
      }
    }
  )*}
}

macro_rules! trivial_copy {
  ($($ty:ty $(where [$($targs:tt)*])?),* $(,)?) => {$(
    unsafe impl<$($($targs)*)?> MoveNew for $ty {
      unsafe fn move_new(
        src: Pin<MoveRef<Self>>,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let src = Pin::into_inner_unchecked(src);
        let this = Pin::into_inner_unchecked(this);
        this.write(MoveRef::into_inner(src));
      }
    }

    impl<$($($targs)*)?> Swap for $ty {
      fn swap_with(self: Pin<&mut Self>, that: Pin<&mut Self>) {
        unsafe {
          let zelf = Pin::into_inner_unchecked(self);
          let that = Pin::into_inner_unchecked(that);
          mem::swap(zelf, that);
        }
      }
    }

    unsafe impl<$($($targs)*)?> CopyNew for $ty where Self: Clone {
      unsafe fn copy_new(
        src: &Self,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let this = Pin::into_inner_unchecked(this);
        this.write(src.clone());
      }
    }
  )*}
}

trivial_move! {
  &mut T where [T: ?Sized],

  core::sync::atomic::AtomicI8,
  core::sync::atomic::AtomicI16,
  core::sync::atomic::AtomicI32,
  core::sync::atomic::AtomicI64,
  core::sync::atomic::AtomicIsize,
  core::sync::atomic::AtomicU8,
  core::sync::atomic::AtomicU16,
  core::sync::atomic::AtomicU32,
  core::sync::atomic::AtomicU64,
  core::sync::atomic::AtomicUsize,
  core::sync::atomic::AtomicPtr<T> where [T],
}

trivial_copy! {
  (), char, bool,
  i8, i16, i32, i64, i128, isize,
  u8, u16, u32, u64, u128, usize,

  &T where [T: ?Sized],
  *const T where [T: ?Sized],
  *mut T where [T: ?Sized],

  core::alloc::Layout,

  core::cell::UnsafeCell<T> where [T],
  core::cell::Cell<T> where [T],
  core::cell::RefCell<T> where [T],
  core::cell::Ref<'_, T> where [T],
  core::cell::RefMut<'_, T> where [T],

  core::marker::PhantomData<T> where [T: ?Sized],
  core::marker::PhantomPinned,

  core::mem::Discriminant<T> where [T],
  core::mem::ManuallyDrop<T> where [T],
  core::mem::MaybeUninit<T> where [T],

  core::num::NonZeroI8,
  core::num::NonZeroI16,
  core::num::NonZeroI32,
  core::num::NonZeroI64,
  core::num::NonZeroI128,
  core::num::NonZeroIsize,
  core::num::NonZeroU8,
  core::num::NonZeroU16,
  core::num::NonZeroU32,
  core::num::NonZeroU64,
  core::num::NonZeroU128,
  core::num::NonZeroUsize,
  core::num::Wrapping<T> where [T],

  core::option::Option<T> where [T],

  core::pin::Pin<T> where [T],
  core::ptr::NonNull<T> where [T],

  core::result::Result<T, E> where [T, E],

  core::time::Duration,
}

#[cfg(feature = "std")]
trivial_copy! {
  alloc::boxed::Box<T> where [T],

  alloc::collections::binary_heap::BinaryHeap<T> where [T],
  alloc::collections::btree_map::BTreeMap<K, V> where [K, V],
  alloc::collections::btree_set::BTreeSet<T> where [T],
  alloc::collections::linked_list::LinkedList<T> where [T],
  alloc::collections::vec_deque::VecDeque<T> where [T],

  alloc::rc::Rc<T> where [T],
  alloc::rc::Weak<T> where [T],
  alloc::sync::Arc<T> where [T],
  alloc::sync::Weak<T> where [T],

  alloc::string::String,
  alloc::vec::Vec<T> where [T],
}
<commit_msg>Fix `alloc` impls not being enabled correctly<commit_after>// Copyright 2021 Google LLC
//
// 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.

//! Trivial impls for `std` types.

use core::mem;
use core::mem::MaybeUninit;
use core::pin::Pin;

use crate::move_ref::MoveRef;
use crate::new::CopyNew;
use crate::new::MoveNew;
use crate::new::Swap;

macro_rules! trivial_move {
  ($($ty:ty $(where [$($targs:tt)*])?),* $(,)?) => {$(
    unsafe impl<$($($targs)*)?> MoveNew for $ty {
      unsafe fn move_new(
        src: Pin<MoveRef<Self>>,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let src = Pin::into_inner_unchecked(src);
        let this = Pin::into_inner_unchecked(this);
        this.write(MoveRef::into_inner(src));
      }
    }

    impl<$($($targs)*)?> Swap for $ty {
      fn swap_with(self: Pin<&mut Self>, that: Pin<&mut Self>) {
        unsafe {
          let zelf = Pin::into_inner_unchecked(self);
          let that = Pin::into_inner_unchecked(that);
          mem::swap(zelf, that);
        }
      }
    }
  )*}
}

macro_rules! trivial_copy {
  ($($ty:ty $(where [$($targs:tt)*])?),* $(,)?) => {$(
    unsafe impl<$($($targs)*)?> MoveNew for $ty {
      unsafe fn move_new(
        src: Pin<MoveRef<Self>>,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let src = Pin::into_inner_unchecked(src);
        let this = Pin::into_inner_unchecked(this);
        this.write(MoveRef::into_inner(src));
      }
    }

    impl<$($($targs)*)?> Swap for $ty {
      fn swap_with(self: Pin<&mut Self>, that: Pin<&mut Self>) {
        unsafe {
          let zelf = Pin::into_inner_unchecked(self);
          let that = Pin::into_inner_unchecked(that);
          mem::swap(zelf, that);
        }
      }
    }

    unsafe impl<$($($targs)*)?> CopyNew for $ty where Self: Clone {
      unsafe fn copy_new(
        src: &Self,
        this: Pin<&mut MaybeUninit<Self>>,
      ) {
        let this = Pin::into_inner_unchecked(this);
        this.write(src.clone());
      }
    }
  )*}
}

trivial_move! {
  &mut T where [T: ?Sized],

  core::sync::atomic::AtomicI8,
  core::sync::atomic::AtomicI16,
  core::sync::atomic::AtomicI32,
  core::sync::atomic::AtomicI64,
  core::sync::atomic::AtomicIsize,
  core::sync::atomic::AtomicU8,
  core::sync::atomic::AtomicU16,
  core::sync::atomic::AtomicU32,
  core::sync::atomic::AtomicU64,
  core::sync::atomic::AtomicUsize,
  core::sync::atomic::AtomicPtr<T> where [T],
}

trivial_copy! {
  (), char, bool,
  i8, i16, i32, i64, i128, isize,
  u8, u16, u32, u64, u128, usize,

  &T where [T: ?Sized],
  *const T where [T: ?Sized],
  *mut T where [T: ?Sized],

  core::alloc::Layout,

  core::cell::UnsafeCell<T> where [T],
  core::cell::Cell<T> where [T],
  core::cell::RefCell<T> where [T],
  core::cell::Ref<'_, T> where [T],
  core::cell::RefMut<'_, T> where [T],

  core::marker::PhantomData<T> where [T: ?Sized],
  core::marker::PhantomPinned,

  core::mem::Discriminant<T> where [T],
  core::mem::ManuallyDrop<T> where [T],
  core::mem::MaybeUninit<T> where [T],

  core::num::NonZeroI8,
  core::num::NonZeroI16,
  core::num::NonZeroI32,
  core::num::NonZeroI64,
  core::num::NonZeroI128,
  core::num::NonZeroIsize,
  core::num::NonZeroU8,
  core::num::NonZeroU16,
  core::num::NonZeroU32,
  core::num::NonZeroU64,
  core::num::NonZeroU128,
  core::num::NonZeroUsize,
  core::num::Wrapping<T> where [T],

  core::option::Option<T> where [T],

  core::pin::Pin<T> where [T],
  core::ptr::NonNull<T> where [T],

  core::result::Result<T, E> where [T, E],

  core::time::Duration,
}

#[cfg(feature = "alloc")]
trivial_copy! {
  alloc::boxed::Box<T> where [T],

  alloc::collections::binary_heap::BinaryHeap<T> where [T],
  alloc::collections::btree_map::BTreeMap<K, V> where [K, V],
  alloc::collections::btree_set::BTreeSet<T> where [T],
  alloc::collections::linked_list::LinkedList<T> where [T],
  alloc::collections::vec_deque::VecDeque<T> where [T],

  alloc::rc::Rc<T> where [T],
  alloc::rc::Weak<T> where [T],
  alloc::sync::Arc<T> where [T],
  alloc::sync::Weak<T> where [T],

  alloc::string::String,
  alloc::vec::Vec<T> where [T],
}
<|endoftext|>


<commit_before>//! A small TCP server to handle collection of diagnostics information in a
//! cross-platform way for the `cargo fix` command.

use std::collections::HashSet;
use std::env;
use std::io::{BufReader, Read, Write};
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};

use failure::{Error, ResultExt};
use serde_json;

use util::{Config, ProcessBuilder};
use util::errors::CargoResult;

const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER";
const PLEASE_REPORT_THIS_BUG: &str =
    "\
     This likely indicates a bug in either rustc or cargo itself,\n\
     and we would appreciate a bug report! You're likely to see \n\
     a number of compiler warnings after this message which cargo\n\
     attempted to fix but failed. If you could open an issue at\n\
     https://github.com/rust-lang/cargo/issues\n\
     quoting the full output of this command we'd be very appreciative!\n\n\
     ";

#[derive(Deserialize, Serialize)]
pub enum Message {
    Fixing {
        file: String,
        fixes: u32,
    },
    FixFailed {
        files: Vec<String>,
        krate: Option<String>,
    },
    ReplaceFailed {
        file: String,
        message: String,
    },
    PreviewNotFound {
        file: String,
        edition: String,
    },
    EditionAlreadyEnabled {
        file: String,
        edition: String,
    },
    IdiomEditionMismatch {
        file: String,
        idioms: String,
        edition: Option<String>,
    },
}

impl Message {
    pub fn post(&self) -> Result<(), Error> {
        let addr = env::var(DIAGNOSICS_SERVER_VAR)
            .context("diagnostics collector misconfigured")?;
        let mut client =
            TcpStream::connect(&addr).context("failed to connect to parent diagnostics target")?;

        let s = serde_json::to_string(self).context("failed to serialize message")?;
        client
            .write_all(s.as_bytes())
            .context("failed to write message to diagnostics target")?;
        client
            .shutdown(Shutdown::Write)
            .context("failed to shutdown")?;

        let mut tmp = Vec::new();
        client
            .read_to_end(&mut tmp)
            .context("failed to receive a disconnect")?;

        Ok(())
    }
}

pub struct DiagnosticPrinter<'a> {
    config: &'a Config,
    preview_not_found: HashSet<String>,
    edition_already_enabled: HashSet<String>,
    idiom_mismatch: HashSet<String>,
}

impl<'a> DiagnosticPrinter<'a> {
    pub fn new(config: &'a Config) -> DiagnosticPrinter<'a> {
        DiagnosticPrinter {
            config,
            preview_not_found: HashSet::new(),
            edition_already_enabled: HashSet::new(),
            idiom_mismatch: HashSet::new(),
        }
    }

    pub fn print(&mut self, msg: &Message) -> CargoResult<()> {
        match msg {
            Message::Fixing { file, fixes } => {
                let msg = if *fixes == 1 { "fix" } else { "fixes" };
                let msg = format!("{} ({} {})", file, fixes, msg);
                self.config.shell().status("Fixing", msg)
            }
            Message::ReplaceFailed { file, message } => {
                let msg = format!("error applying suggestions to `{}`\n", file);
                self.config.shell().warn(&msg)?;
                write!(
                    self.config.shell().err(),
                    "The full error message was:\n\n> {}\n\n",
                    message,
                )?;
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::FixFailed { files, krate } => {
                if let Some(ref krate) = *krate {
                    self.config.shell().warn(&format!(
                        "failed to automatically apply fixes suggested by rustc \
                         to crate `{}`",
                        krate,
                    ))?;
                } else {
                    self.config.shell().warn(
                        "failed to automatically apply fixes suggested by rustc"
                    )?;
                }
                if !files.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "\nafter fixes were automatically applied the compiler \
                         reported errors within these files:\n"
                    )?;
                    for file in files {
                        writeln!(self.config.shell().err(), "  * {}", file)?;
                    }
                    writeln!(self.config.shell().err())?;
                }
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::PreviewNotFound { file, edition } => {
                // By default we're fixing a lot of things concurrently, don't
                // warn about the same file multiple times.
                if !self.preview_not_found.insert(file.clone()) {
                    return Ok(())
                }
                self.config.shell().warn(&format!(
                    "failed to find `#![feature(rust_{}_preview)]` in `{}`\n\
                     this may cause `cargo fix` to not be able to fix all\n\
                     issues in preparation for the {0} edition",
                    edition,
                    file,
                ))?;
                Ok(())
            }
            Message::EditionAlreadyEnabled { file, edition } => {
                // Like above, only warn once per file
                if !self.edition_already_enabled.insert(file.clone()) {
                    return Ok(())
                }

                let msg = format!(
                    "\
cannot prepare for the {} edition when it is enabled, so cargo cannot
automatically fix errors in `{}`

To prepare for the {0} edition you should first remove `edition = '{0}'` from
your `Cargo.toml` and then rerun this command. Once all warnings have been fixed
then you can re-enable the `edition` key in `Cargo.toml`. For some more
information about transitioning to the {0} edition see:

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning.html
",
                    edition,
                    file,
                );
                self.config.shell().error(&msg)?;
                Ok(())
            }
            Message::IdiomEditionMismatch { file, idioms, edition } => {
                // Same as above
                if !self.idiom_mismatch.insert(file.clone()) {
                    return Ok(())
                }
                self.config.shell().error(&format!(
                    "\
cannot migrate to the idioms of the {} edition for `{}`
because it is compiled {}, which doesn't match {0}

consider migrating to the {0} edition by adding `edition = '{0}'` to
`Cargo.toml` and then rerunning this command; a more detailed transition
guide can be found at

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-your-code-to-a-new-edition.html
",
                    idioms,
                    file,
                    match edition {
                        Some(s) => format!("with the {} edition", s),
                        None => "without an edition".to_string(),
                    },
                ))?;
                Ok(())
            }
        }
    }
}

#[derive(Debug)]
pub struct RustfixDiagnosticServer {
    listener: TcpListener,
    addr: SocketAddr,
}

pub struct StartedServer {
    addr: SocketAddr,
    done: Arc<AtomicBool>,
    thread: Option<JoinHandle<()>>,
}

impl RustfixDiagnosticServer {
    pub fn new() -> Result<Self, Error> {
        let listener = TcpListener::bind("127.0.0.1:0")
            .with_context(|_| "failed to bind TCP listener to manage locking")?;
        let addr = listener.local_addr()?;

        Ok(RustfixDiagnosticServer { listener, addr })
    }

    pub fn configure(&self, process: &mut ProcessBuilder) {
        process.env(DIAGNOSICS_SERVER_VAR, self.addr.to_string());
    }

    pub fn start<F>(self, on_message: F) -> Result<StartedServer, Error>
    where
        F: Fn(Message) + Send + 'static,
    {
        let addr = self.addr;
        let done = Arc::new(AtomicBool::new(false));
        let done2 = done.clone();
        let thread = thread::spawn(move || {
            self.run(&on_message, &done2);
        });

        Ok(StartedServer {
            addr,
            thread: Some(thread),
            done,
        })
    }

    fn run(self, on_message: &Fn(Message), done: &AtomicBool) {
        while let Ok((client, _)) = self.listener.accept() {
            let client = BufReader::new(client);
            match serde_json::from_reader(client) {
                Ok(message) => on_message(message),
                Err(e) => warn!("invalid diagnostics message: {}", e),
            }
            if done.load(Ordering::SeqCst) {
                break
            }
        }
    }
}

impl Drop for StartedServer {
    fn drop(&mut self) {
        self.done.store(true, Ordering::SeqCst);
        // Ignore errors here as this is largely best-effort
        if TcpStream::connect(&self.addr).is_err() {
            return;
        }
        drop(self.thread.take().unwrap().join());
    }
}
<commit_msg>Auto merge of #5900 - orium:fix-broken-url, r=alexcrichton<commit_after>//! A small TCP server to handle collection of diagnostics information in a
//! cross-platform way for the `cargo fix` command.

use std::collections::HashSet;
use std::env;
use std::io::{BufReader, Read, Write};
use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};

use failure::{Error, ResultExt};
use serde_json;

use util::{Config, ProcessBuilder};
use util::errors::CargoResult;

const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER";
const PLEASE_REPORT_THIS_BUG: &str =
    "\
     This likely indicates a bug in either rustc or cargo itself,\n\
     and we would appreciate a bug report! You're likely to see \n\
     a number of compiler warnings after this message which cargo\n\
     attempted to fix but failed. If you could open an issue at\n\
     https://github.com/rust-lang/cargo/issues\n\
     quoting the full output of this command we'd be very appreciative!\n\n\
     ";

#[derive(Deserialize, Serialize)]
pub enum Message {
    Fixing {
        file: String,
        fixes: u32,
    },
    FixFailed {
        files: Vec<String>,
        krate: Option<String>,
    },
    ReplaceFailed {
        file: String,
        message: String,
    },
    PreviewNotFound {
        file: String,
        edition: String,
    },
    EditionAlreadyEnabled {
        file: String,
        edition: String,
    },
    IdiomEditionMismatch {
        file: String,
        idioms: String,
        edition: Option<String>,
    },
}

impl Message {
    pub fn post(&self) -> Result<(), Error> {
        let addr = env::var(DIAGNOSICS_SERVER_VAR)
            .context("diagnostics collector misconfigured")?;
        let mut client =
            TcpStream::connect(&addr).context("failed to connect to parent diagnostics target")?;

        let s = serde_json::to_string(self).context("failed to serialize message")?;
        client
            .write_all(s.as_bytes())
            .context("failed to write message to diagnostics target")?;
        client
            .shutdown(Shutdown::Write)
            .context("failed to shutdown")?;

        let mut tmp = Vec::new();
        client
            .read_to_end(&mut tmp)
            .context("failed to receive a disconnect")?;

        Ok(())
    }
}

pub struct DiagnosticPrinter<'a> {
    config: &'a Config,
    preview_not_found: HashSet<String>,
    edition_already_enabled: HashSet<String>,
    idiom_mismatch: HashSet<String>,
}

impl<'a> DiagnosticPrinter<'a> {
    pub fn new(config: &'a Config) -> DiagnosticPrinter<'a> {
        DiagnosticPrinter {
            config,
            preview_not_found: HashSet::new(),
            edition_already_enabled: HashSet::new(),
            idiom_mismatch: HashSet::new(),
        }
    }

    pub fn print(&mut self, msg: &Message) -> CargoResult<()> {
        match msg {
            Message::Fixing { file, fixes } => {
                let msg = if *fixes == 1 { "fix" } else { "fixes" };
                let msg = format!("{} ({} {})", file, fixes, msg);
                self.config.shell().status("Fixing", msg)
            }
            Message::ReplaceFailed { file, message } => {
                let msg = format!("error applying suggestions to `{}`\n", file);
                self.config.shell().warn(&msg)?;
                write!(
                    self.config.shell().err(),
                    "The full error message was:\n\n> {}\n\n",
                    message,
                )?;
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::FixFailed { files, krate } => {
                if let Some(ref krate) = *krate {
                    self.config.shell().warn(&format!(
                        "failed to automatically apply fixes suggested by rustc \
                         to crate `{}`",
                        krate,
                    ))?;
                } else {
                    self.config.shell().warn(
                        "failed to automatically apply fixes suggested by rustc"
                    )?;
                }
                if !files.is_empty() {
                    writeln!(
                        self.config.shell().err(),
                        "\nafter fixes were automatically applied the compiler \
                         reported errors within these files:\n"
                    )?;
                    for file in files {
                        writeln!(self.config.shell().err(), "  * {}", file)?;
                    }
                    writeln!(self.config.shell().err())?;
                }
                write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
                Ok(())
            }
            Message::PreviewNotFound { file, edition } => {
                // By default we're fixing a lot of things concurrently, don't
                // warn about the same file multiple times.
                if !self.preview_not_found.insert(file.clone()) {
                    return Ok(())
                }
                self.config.shell().warn(&format!(
                    "failed to find `#![feature(rust_{}_preview)]` in `{}`\n\
                     this may cause `cargo fix` to not be able to fix all\n\
                     issues in preparation for the {0} edition",
                    edition,
                    file,
                ))?;
                Ok(())
            }
            Message::EditionAlreadyEnabled { file, edition } => {
                // Like above, only warn once per file
                if !self.edition_already_enabled.insert(file.clone()) {
                    return Ok(())
                }

                let msg = format!(
                    "\
cannot prepare for the {} edition when it is enabled, so cargo cannot
automatically fix errors in `{}`

To prepare for the {0} edition you should first remove `edition = '{0}'` from
your `Cargo.toml` and then rerun this command. Once all warnings have been fixed
then you can re-enable the `edition` key in `Cargo.toml`. For some more
information about transitioning to the {0} edition see:

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-your-code-to-a-new-edition.html
",
                    edition,
                    file,
                );
                self.config.shell().error(&msg)?;
                Ok(())
            }
            Message::IdiomEditionMismatch { file, idioms, edition } => {
                // Same as above
                if !self.idiom_mismatch.insert(file.clone()) {
                    return Ok(())
                }
                self.config.shell().error(&format!(
                    "\
cannot migrate to the idioms of the {} edition for `{}`
because it is compiled {}, which doesn't match {0}

consider migrating to the {0} edition by adding `edition = '{0}'` to
`Cargo.toml` and then rerunning this command; a more detailed transition
guide can be found at

  https://rust-lang-nursery.github.io/edition-guide/editions/transitioning-your-code-to-a-new-edition.html
",
                    idioms,
                    file,
                    match edition {
                        Some(s) => format!("with the {} edition", s),
                        None => "without an edition".to_string(),
                    },
                ))?;
                Ok(())
            }
        }
    }
}

#[derive(Debug)]
pub struct RustfixDiagnosticServer {
    listener: TcpListener,
    addr: SocketAddr,
}

pub struct StartedServer {
    addr: SocketAddr,
    done: Arc<AtomicBool>,
    thread: Option<JoinHandle<()>>,
}

impl RustfixDiagnosticServer {
    pub fn new() -> Result<Self, Error> {
        let listener = TcpListener::bind("127.0.0.1:0")
            .with_context(|_| "failed to bind TCP listener to manage locking")?;
        let addr = listener.local_addr()?;

        Ok(RustfixDiagnosticServer { listener, addr })
    }

    pub fn configure(&self, process: &mut ProcessBuilder) {
        process.env(DIAGNOSICS_SERVER_VAR, self.addr.to_string());
    }

    pub fn start<F>(self, on_message: F) -> Result<StartedServer, Error>
    where
        F: Fn(Message) + Send + 'static,
    {
        let addr = self.addr;
        let done = Arc::new(AtomicBool::new(false));
        let done2 = done.clone();
        let thread = thread::spawn(move || {
            self.run(&on_message, &done2);
        });

        Ok(StartedServer {
            addr,
            thread: Some(thread),
            done,
        })
    }

    fn run(self, on_message: &Fn(Message), done: &AtomicBool) {
        while let Ok((client, _)) = self.listener.accept() {
            let client = BufReader::new(client);
            match serde_json::from_reader(client) {
                Ok(message) => on_message(message),
                Err(e) => warn!("invalid diagnostics message: {}", e),
            }
            if done.load(Ordering::SeqCst) {
                break
            }
        }
    }
}

impl Drop for StartedServer {
    fn drop(&mut self) {
        self.done.store(true, Ordering::SeqCst);
        // Ignore errors here as this is largely best-effort
        if TcpStream::connect(&self.addr).is_err() {
            return;
        }
        drop(self.thread.take().unwrap().join());
    }
}
<|endoftext|>


<commit_before>mod support;
use support::*;

#[test]
fn test_response_text() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/text", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(5));

    let body = res.text().unwrap();
    assert_eq!(b"Hello", body.as_bytes());
}

#[test]
fn test_response_non_utf_8_text() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .header("content-type", "text/plain; charset=gbk")
            .body(b"\xc4\xe3\xba\xc3"[..].into())
            .unwrap()
    });

    let url = format!("http://{}/text", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(4));

    let body = res.text().unwrap();
    assert_eq!("你好", &body);
    assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
}

#[test]
#[cfg(feature = "json")]
fn test_response_json() {
    let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) });

    let url = format!("http://{}/json", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(7));

    let body = res.json::<String>().unwrap();
    assert_eq!("Hello", body);
}

#[test]
fn test_response_copy_to() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/1", server.addr());
    let mut res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);

    let mut dst = Vec::new();
    res.copy_to(&mut dst).unwrap();
    assert_eq!(dst, b"Hello");
}

#[test]
fn test_get() {
    let server = server::http(move |_req| async { http::Response::default() });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.remote_addr(), Some(server.addr()));

    assert_eq!(res.text().unwrap().len(), 0)
}

#[test]
fn test_post() {
    let server = server::http(move |req| async move {
        assert_eq!(req.method(), "POST");
        assert_eq!(req.headers()["content-length"], "5");

        let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
        assert_eq!(&*data, b"Hello");

        http::Response::default()
    });

    let url = format!("http://{}/2", server.addr());
    let res = reqwest::blocking::Client::new()
        .post(&url)
        .body("Hello")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_post_form() {
    let server = server::http(move |req| async move {
        assert_eq!(req.method(), "POST");
        assert_eq!(req.headers()["content-length"], "24");
        assert_eq!(
            req.headers()["content-type"],
            "application/x-www-form-urlencoded"
        );

        let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
        assert_eq!(&*data, b"hello=world&sean=monstar");

        http::Response::default()
    });

    let form = &[("hello", "world"), ("sean", "monstar")];

    let url = format!("http://{}/form", server.addr());
    let res = reqwest::blocking::Client::new()
        .post(&url)
        .form(form)
        .send()
        .expect("request send");

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

/// Calling `Response::error_for_status`` on a response with status in 4xx
/// returns a error.
#[test]
fn test_error_for_status_4xx() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .status(400)
            .body(Default::default())
            .unwrap()
    });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    let err = res.error_for_status().unwrap_err();
    assert!(err.is_status());
    assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
}

/// Calling `Response::error_for_status`` on a response with status in 5xx
/// returns a error.
#[test]
fn test_error_for_status_5xx() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .status(500)
            .body(Default::default())
            .unwrap()
    });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    let err = res.error_for_status().unwrap_err();
    assert!(err.is_status());
    assert_eq!(
        err.status(),
        Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
    );
}

#[test]
fn test_default_headers() {
    let server = server::http(move |req| async move {
        assert_eq!(req.headers()["reqwest-test"], "orly");
        http::Response::default()
    });

    let mut headers = http::HeaderMap::with_capacity(1);
    headers.insert("reqwest-test", "orly".parse().unwrap());
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/1", server.addr());
    let res = client.get(&url).send().unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_override_default_headers() {
    let server = server::http(move |req| {
        async move {
            // not 'iamatoken'
            assert_eq!(req.headers()[&http::header::AUTHORIZATION], "secret");
            http::Response::default()
        }
    });

    let mut headers = http::HeaderMap::with_capacity(1);
    headers.insert(
        http::header::AUTHORIZATION,
        http::header::HeaderValue::from_static("iamatoken"),
    );
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/3", server.addr());
    let res = client
        .get(&url)
        .header(
            http::header::AUTHORIZATION,
            http::header::HeaderValue::from_static("secret"),
        )
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_appended_headers_not_overwritten() {
    let server = server::http(move |req| async move {
        let mut accepts = req.headers().get_all("accept").into_iter();
        assert_eq!(accepts.next().unwrap(), "application/json");
        assert_eq!(accepts.next().unwrap(), "application/json+hal");
        assert_eq!(accepts.next(), None);

        http::Response::default()
    });

    let client = reqwest::blocking::Client::new();

    let url = format!("http://{}/4", server.addr());
    let res = client
        .get(&url)
        .header(header::ACCEPT, "application/json")
        .header(header::ACCEPT, "application/json+hal")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);

    // make sure this also works with default headers
    use reqwest::header;
    let mut headers = header::HeaderMap::with_capacity(1);
    headers.insert(
        header::ACCEPT,
        header::HeaderValue::from_static("text/html"),
    );
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/4", server.addr());
    let res = client
        .get(&url)
        .header(header::ACCEPT, "application/json")
        .header(header::ACCEPT, "application/json+hal")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
#[should_panic]
fn test_blocking_inside_a_runtime() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/text", server.addr());

    let mut rt = tokio::runtime::Builder::new().build().expect("new rt");

    rt.block_on(async move {
        let _should_panic = reqwest::blocking::get(&url);
    });
}
<commit_msg>Disable test_blocking_inside_a_runtime without cfg(debug_assertions)<commit_after>mod support;
use support::*;

#[test]
fn test_response_text() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/text", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(5));

    let body = res.text().unwrap();
    assert_eq!(b"Hello", body.as_bytes());
}

#[test]
fn test_response_non_utf_8_text() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .header("content-type", "text/plain; charset=gbk")
            .body(b"\xc4\xe3\xba\xc3"[..].into())
            .unwrap()
    });

    let url = format!("http://{}/text", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(4));

    let body = res.text().unwrap();
    assert_eq!("你好", &body);
    assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
}

#[test]
#[cfg(feature = "json")]
fn test_response_json() {
    let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) });

    let url = format!("http://{}/json", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.content_length(), Some(7));

    let body = res.json::<String>().unwrap();
    assert_eq!("Hello", body);
}

#[test]
fn test_response_copy_to() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/1", server.addr());
    let mut res = reqwest::blocking::get(&url).unwrap();
    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);

    let mut dst = Vec::new();
    res.copy_to(&mut dst).unwrap();
    assert_eq!(dst, b"Hello");
}

#[test]
fn test_get() {
    let server = server::http(move |_req| async { http::Response::default() });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
    assert_eq!(res.remote_addr(), Some(server.addr()));

    assert_eq!(res.text().unwrap().len(), 0)
}

#[test]
fn test_post() {
    let server = server::http(move |req| async move {
        assert_eq!(req.method(), "POST");
        assert_eq!(req.headers()["content-length"], "5");

        let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
        assert_eq!(&*data, b"Hello");

        http::Response::default()
    });

    let url = format!("http://{}/2", server.addr());
    let res = reqwest::blocking::Client::new()
        .post(&url)
        .body("Hello")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_post_form() {
    let server = server::http(move |req| async move {
        assert_eq!(req.method(), "POST");
        assert_eq!(req.headers()["content-length"], "24");
        assert_eq!(
            req.headers()["content-type"],
            "application/x-www-form-urlencoded"
        );

        let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
        assert_eq!(&*data, b"hello=world&sean=monstar");

        http::Response::default()
    });

    let form = &[("hello", "world"), ("sean", "monstar")];

    let url = format!("http://{}/form", server.addr());
    let res = reqwest::blocking::Client::new()
        .post(&url)
        .form(form)
        .send()
        .expect("request send");

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

/// Calling `Response::error_for_status`` on a response with status in 4xx
/// returns a error.
#[test]
fn test_error_for_status_4xx() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .status(400)
            .body(Default::default())
            .unwrap()
    });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    let err = res.error_for_status().unwrap_err();
    assert!(err.is_status());
    assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
}

/// Calling `Response::error_for_status`` on a response with status in 5xx
/// returns a error.
#[test]
fn test_error_for_status_5xx() {
    let server = server::http(move |_req| async {
        http::Response::builder()
            .status(500)
            .body(Default::default())
            .unwrap()
    });

    let url = format!("http://{}/1", server.addr());
    let res = reqwest::blocking::get(&url).unwrap();

    let err = res.error_for_status().unwrap_err();
    assert!(err.is_status());
    assert_eq!(
        err.status(),
        Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
    );
}

#[test]
fn test_default_headers() {
    let server = server::http(move |req| async move {
        assert_eq!(req.headers()["reqwest-test"], "orly");
        http::Response::default()
    });

    let mut headers = http::HeaderMap::with_capacity(1);
    headers.insert("reqwest-test", "orly".parse().unwrap());
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/1", server.addr());
    let res = client.get(&url).send().unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_override_default_headers() {
    let server = server::http(move |req| {
        async move {
            // not 'iamatoken'
            assert_eq!(req.headers()[&http::header::AUTHORIZATION], "secret");
            http::Response::default()
        }
    });

    let mut headers = http::HeaderMap::with_capacity(1);
    headers.insert(
        http::header::AUTHORIZATION,
        http::header::HeaderValue::from_static("iamatoken"),
    );
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/3", server.addr());
    let res = client
        .get(&url)
        .header(
            http::header::AUTHORIZATION,
            http::header::HeaderValue::from_static("secret"),
        )
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[test]
fn test_appended_headers_not_overwritten() {
    let server = server::http(move |req| async move {
        let mut accepts = req.headers().get_all("accept").into_iter();
        assert_eq!(accepts.next().unwrap(), "application/json");
        assert_eq!(accepts.next().unwrap(), "application/json+hal");
        assert_eq!(accepts.next(), None);

        http::Response::default()
    });

    let client = reqwest::blocking::Client::new();

    let url = format!("http://{}/4", server.addr());
    let res = client
        .get(&url)
        .header(header::ACCEPT, "application/json")
        .header(header::ACCEPT, "application/json+hal")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);

    // make sure this also works with default headers
    use reqwest::header;
    let mut headers = header::HeaderMap::with_capacity(1);
    headers.insert(
        header::ACCEPT,
        header::HeaderValue::from_static("text/html"),
    );
    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let url = format!("http://{}/4", server.addr());
    let res = client
        .get(&url)
        .header(header::ACCEPT, "application/json")
        .header(header::ACCEPT, "application/json+hal")
        .send()
        .unwrap();

    assert_eq!(res.url().as_str(), &url);
    assert_eq!(res.status(), reqwest::StatusCode::OK);
}

#[cfg_attr(not(debug_assertions), ignore)]
#[test]
#[should_panic]
fn test_blocking_inside_a_runtime() {
    let server = server::http(move |_req| async { http::Response::new("Hello".into()) });

    let url = format!("http://{}/text", server.addr());

    let mut rt = tokio::runtime::Builder::new().build().expect("new rt");

    rt.block_on(async move {
        let _should_panic = reqwest::blocking::get(&url);
    });
}
<|endoftext|>


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

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert($name, Help {
            short: $short,
            long: concat!($long, "\n ")
        });
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name).help(helps[name].short).long_help(
            helps[name].long,
        )
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&["f", "file", "d", "directory", "l", "symlink"])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num"),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path"))
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped).");
    doc!(h, "no-ignore"
        , "Do not respect .(git)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.*ignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains both upper- and lowercase characters (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains both upper- and lowercase characters (smart case).");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descent into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "file-type"
        , "Filter by type: f(ile), d(irectory), (sym)l(ink)"
        , "Filter the search by type:\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching"
        , "Set number of threads to use for searching (default: number of available CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to\
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");

    h
}
<commit_msg>Fix description of smart-case in help text<commit_after>use std::collections::HashMap;

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert($name, Help {
            short: $short,
            long: concat!($long, "\n ")
        });
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name).help(helps[name].short).long_help(
            helps[name].long,
        )
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&["f", "file", "d", "directory", "l", "symlink"])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num"),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path"))
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped).");
    doc!(h, "no-ignore"
        , "Do not respect .(git)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.*ignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descent into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "file-type"
        , "Filter by type: f(ile), d(irectory), (sym)l(ink)"
        , "Filter the search by type:\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching"
        , "Set number of threads to use for searching (default: number of available CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to\
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");

    h
}
<|endoftext|>


<commit_before>use yaml::Yaml;
use yaml::yaml::{Array, Hash};
use regex::{Captures, Regex};

use parameter::ParamMap;

pub type ProcessorResult = Result<Option<Yaml>, String>;

pub fn process_yaml(yaml: &mut Yaml, parameters: &ParamMap) -> ProcessorResult {
    match yaml {
        &mut Yaml::Array(ref mut array) => process_array(array, parameters),
        &mut Yaml::Hash(ref mut hash) => process_hash(hash, parameters),
        &mut Yaml::String(ref mut string) => process_string(string, parameters),
        _ => Ok(None),
    }
}

fn process_array(array: &mut Array, parameters: &ParamMap) -> ProcessorResult {
    for value in array {
        match process_yaml(value, parameters) {
            Ok(Some(new_value)) => *value = new_value,
            Err(error) => return Err(error),
            _ => {},
        }
    }

    Ok(None)
}

fn process_hash(hash: &mut Hash, parameters: &ParamMap) -> ProcessorResult {
    for (_, value) in hash {
        match process_yaml(value, parameters) {
            Ok(Some(new_value)) => *value = new_value,
            Err(error) => return Err(error),
            _ => {},
        }
    }

    Ok(None)
}

fn process_string(string: &mut String, parameters: &ParamMap) -> ProcessorResult {
    lazy_static! {
        static ref LITERAL_INTERPOLATION: Regex = Regex::new(
            r"\$\({2}(.*)\){2}"
        ).expect("Failed to compile regex.");
    }

    lazy_static! {
        static ref STRING_INTERPOLATION: Regex = Regex::new(
            r"\$\((.*)\)"
        ).expect("Failed to compile regex.");
    }

    let interpolate = |captures: &Captures| -> String {
        let key = captures.at(1).expect("Failed to extract regex capture group.");

        match parameters.get(key) {
            Some(parameter) => parameter.value.clone().unwrap_or("~".to_owned()),
            None => captures.at(0).expect("Failed to extract regex match.").to_owned(),
        }
    };

    let replacement = LITERAL_INTERPOLATION.replace_all(string, &interpolate);

    let contains_literal_replacement = &replacement != string;

    let final_replacement = STRING_INTERPOLATION.replace_all(&replacement, &interpolate);

    let contains_string_replacement = &final_replacement != &replacement;

    if !contains_literal_replacement && !contains_string_replacement {
        Ok(None)
    } else if contains_literal_replacement && !contains_string_replacement {
        Ok(Some(Yaml::from_str(&final_replacement)))
    } else {
        Ok(Some(Yaml::String(final_replacement)))
    }
}
<commit_msg>Makes regex less greedy<commit_after>use yaml::Yaml;
use yaml::yaml::{Array, Hash};
use regex::{Captures, Regex};

use parameter::ParamMap;

pub type ProcessorResult = Result<Option<Yaml>, String>;

pub fn process_yaml(yaml: &mut Yaml, parameters: &ParamMap) -> ProcessorResult {
    match yaml {
        &mut Yaml::Array(ref mut array) => process_array(array, parameters),
        &mut Yaml::Hash(ref mut hash) => process_hash(hash, parameters),
        &mut Yaml::String(ref mut string) => process_string(string, parameters),
        _ => Ok(None),
    }
}

fn process_array(array: &mut Array, parameters: &ParamMap) -> ProcessorResult {
    for value in array {
        match process_yaml(value, parameters) {
            Ok(Some(new_value)) => *value = new_value,
            Err(error) => return Err(error),
            _ => {},
        }
    }

    Ok(None)
}

fn process_hash(hash: &mut Hash, parameters: &ParamMap) -> ProcessorResult {
    for (_, value) in hash {
        match process_yaml(value, parameters) {
            Ok(Some(new_value)) => *value = new_value,
            Err(error) => return Err(error),
            _ => {},
        }
    }

    Ok(None)
}

fn process_string(string: &mut String, parameters: &ParamMap) -> ProcessorResult {
    lazy_static! {
        static ref LITERAL_INTERPOLATION: Regex = Regex::new(
            r"\$\({2}([^\)]*)\){2}"
        ).expect("Failed to compile regex.");
    }

    lazy_static! {
        static ref STRING_INTERPOLATION: Regex = Regex::new(
            r"\$\(([^\)]*)\)"
        ).expect("Failed to compile regex.");
    }

    let interpolate = |captures: &Captures| -> String {
        let key = captures.at(1).expect("Failed to extract regex capture group.");

        match parameters.get(key) {
            Some(parameter) => parameter.value.clone().unwrap_or("~".to_owned()),
            None => captures.at(0).expect("Failed to extract regex match.").to_owned(),
        }
    };

    let replacement = LITERAL_INTERPOLATION.replace_all(string, &interpolate);

    let contains_literal_replacement = &replacement != string;

    let final_replacement = STRING_INTERPOLATION.replace_all(&replacement, &interpolate);

    let contains_string_replacement = &final_replacement != &replacement;

    if !contains_literal_replacement && !contains_string_replacement {
        Ok(None)
    } else if contains_literal_replacement && !contains_string_replacement {
        Ok(Some(Yaml::from_str(&final_replacement)))
    } else {
        Ok(Some(Yaml::String(final_replacement)))
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Read;
use std::ops::RangeInclusive;

use crate::common::Lines;
use crate::Solution;

fn split_nums<'a>(s: &'a str) -> impl Iterator<Item = u32> + 'a {
    s.split(|c: char| !c.is_ascii_digit()).filter_map(|s| {
        if s.is_empty() {
            None
        } else {
            Some(s.parse().unwrap())
        }
    })
}

fn read_input(input: &mut dyn Read) -> (HashMap<String, Vec<RangeInclusive<u32>>>, Vec<Vec<u32>>) {
    let mut lines = Lines::new(input).filter(|s| !s.is_empty());

    let mut rules = HashMap::new();
    let mut tickets = Vec::new();

    while let Some(line) = lines.next() {
        if line.as_str() == "your ticket:" {
            break;
        }

        let colon = line.find(':').unwrap();

        let nums: Vec<_> = split_nums(&line[colon..]).collect();

        let ranges: Vec<_> = nums.chunks_exact(2).map(|s| s[0]..=s[1]).collect();

        rules.insert(line[..colon].to_owned(), ranges);
    }

    while let Some(line) = lines.next() {
        if line.as_str() == "nearby tickets:" {
            continue;
        }

        tickets.push(split_nums(&line).collect());
    }

    (rules, tickets)
}

#[derive(Default)]
pub struct Day16;

impl Solution for Day16 {
    fn part1(&mut self, input: &mut dyn Read) -> String {
        let (rules, tickets) = read_input(input);

        let mut rate = 0;

        for ticket in &tickets[1..] {
            for value in ticket {
                if rules.values().flatten().all(|r| !r.contains(value)) {
                    rate += value;
                }
            }
        }

        rate.to_string()
    }

    fn part2(&mut self, input: &mut dyn Read) -> String {
        let (rules, tickets) = read_input(input);
        let mut tickets = tickets;

        // O(n) operation but it's fine
        let my_ticket = tickets.remove(0);

        // Filter out invalid tickets
        tickets.retain(|t| {
            t.iter()
                .all(|v| rules.values().flatten().any(|r| r.contains(v)))
        });

        let all_fields: HashSet<_> = rules.keys().cloned().collect();

        let mut pos_can_be = vec![all_fields; my_ticket.len()];

        let mut fixed_fields = HashMap::new();

        while fixed_fields.len() != rules.len() {
            for ticket in &tickets {
                for (pos, value) in ticket.iter().enumerate() {
                    if pos_can_be[pos].len() <= 1 {
                        continue;
                    }

                    for (field, ranges) in &rules {
                        // If we already assigned this field, don't continue
                        if fixed_fields.contains_key(field) || !pos_can_be[pos].contains(field) {
                            continue;
                        }

                        if !ranges.iter().any(|r| r.contains(value)) {
                            pos_can_be[pos].remove(field);
                        }
                    }
                }
            }

            // Fix fields that are the only option for a certain spot
            for pos in 0..pos_can_be.len() {
                if pos_can_be[pos].len() == 1 {
                    let field = pos_can_be[pos]
                        .drain()
                        .next()
                        .expect("Safe because if statement");

                    assert!(!fixed_fields.contains_key(&field));

                    for can_be in pos_can_be.iter_mut() {
                        can_be.remove(&field);
                    }

                    fixed_fields.insert(field, pos);
                }
            }

            // Check if there are fields that only fit a single spot
            let mut can_fit: HashMap<_, Vec<usize>> = HashMap::new();

            for (pos, candiates) in pos_can_be.iter().enumerate() {
                for candidate in candiates {
                    can_fit.entry(candidate.clone()).or_default().push(pos);
                }
            }

            for (field, pos) in can_fit.into_iter().filter(|(_, v)| v.len() == 1) {
                let pos = pos[0];
                assert!(!fixed_fields.contains_key(&field));

                pos_can_be[pos].clear();

                fixed_fields.insert(field, pos);
            }
        }

        fixed_fields
            .iter()
            .filter(|(k, _)| k.starts_with("departure"))
            .map(|(_, &v)| my_ticket[v])
            .fold(1u64, |a, b| a * (b as u64))
            .to_string()
    }
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("../samples/16.txt");

    #[test]
    fn sample_part1() {
        test_implementation!(Day16, 1, SAMPLE, 71);
    }
}
<commit_msg>Optimizations for day 16.<commit_after>use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Read;
use std::ops::RangeInclusive;

use crate::common::Lines;
use crate::Solution;

fn split_nums<'a>(s: &'a str) -> impl Iterator<Item = u32> + 'a {
    s.split(|c: char| !c.is_ascii_digit()).filter_map(|s| {
        if s.is_empty() {
            None
        } else {
            Some(s.parse().unwrap())
        }
    })
}

fn read_input(input: &mut dyn Read) -> (HashMap<String, Vec<RangeInclusive<u32>>>, Vec<Vec<u32>>) {
    let mut lines = Lines::new(input).filter(|s| !s.is_empty());

    let mut rules = HashMap::new();
    let mut tickets = Vec::new();

    while let Some(line) = lines.next() {
        if line.as_str() == "your ticket:" {
            break;
        }

        let colon = line.find(':').unwrap();

        let nums: Vec<_> = split_nums(&line[colon..]).collect();

        let ranges: Vec<_> = nums.chunks_exact(2).map(|s| s[0]..=s[1]).collect();

        rules.insert(line[..colon].to_owned(), ranges);
    }

    while let Some(line) = lines.next() {
        if line.as_str() == "nearby tickets:" {
            continue;
        }

        tickets.push(split_nums(&line).collect());
    }

    (rules, tickets)
}

#[derive(Default)]
pub struct Day16;

impl Solution for Day16 {
    fn part1(&mut self, input: &mut dyn Read) -> String {
        let (rules, tickets) = read_input(input);

        let mut rate = 0;

        for ticket in &tickets[1..] {
            for value in ticket {
                if rules.values().flatten().all(|r| !r.contains(value)) {
                    rate += value;
                }
            }
        }

        rate.to_string()
    }

    fn part2(&mut self, input: &mut dyn Read) -> String {
        let (rules, tickets) = read_input(input);
        let mut tickets = tickets;

        // O(n) operation but it's fine
        let my_ticket = tickets.remove(0);

        // Filter out invalid tickets
        tickets.retain(|t| {
            t.iter()
                .all(|v| rules.values().flatten().any(|r| r.contains(v)))
        });

        let all_fields: HashSet<_> = rules.keys().cloned().collect();

        let mut pos_can_be = vec![all_fields; my_ticket.len()];

        let mut fixed_fields = HashMap::new();

        for ticket in &tickets {
            for (field, ranges) in &rules {
                for (pos, value) in ticket.iter().enumerate() {
                    if pos_can_be[pos].len() <= 1 {
                        continue;
                    }

                    if !ranges.iter().any(|r| r.contains(value)) {
                        pos_can_be[pos].remove(field);
                    }
                }
            }
        }

        while fixed_fields.len() != rules.len() {
            // Fix fields that are the only option for a certain spot
            for pos in 0..pos_can_be.len() {
                if pos_can_be[pos].len() == 1 {
                    let field = pos_can_be[pos]
                        .drain()
                        .next()
                        .expect("Safe because if statement");

                    assert!(!fixed_fields.contains_key(&field));

                    for can_be in pos_can_be.iter_mut() {
                        can_be.remove(&field);
                    }

                    fixed_fields.insert(field, pos);
                }
            }

            // Check if there are fields that only fit a single spot
            let mut can_fit: HashMap<_, Vec<usize>> = HashMap::new();

            for (pos, candiates) in pos_can_be.iter().enumerate() {
                for candidate in candiates {
                    can_fit.entry(candidate.clone()).or_default().push(pos);
                }
            }

            for (field, pos) in can_fit.into_iter().filter(|(_, v)| v.len() == 1) {
                let pos = pos[0];
                assert!(!fixed_fields.contains_key(&field));

                pos_can_be[pos].clear();

                fixed_fields.insert(field, pos);
            }
        }

        fixed_fields
            .iter()
            .filter(|(k, _)| k.starts_with("departure"))
            .map(|(_, &v)| my_ticket[v])
            .fold(1u64, |a, b| a * (b as u64))
            .to_string()
    }
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("../samples/16.txt");

    #[test]
    fn sample_part1() {
        test_implementation!(Day16, 1, SAMPLE, 71);
    }
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction strategies) of
/// β-reductions. `Applicative` order will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVALUATION_ORDER: Order = Normal;

#[derive(Debug, PartialEq)]
pub enum Order {
    Normal,
    Applicative
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVALUATION_ORDER {
            Normal => self._beta_once_normal(0),
            Applicative => self._beta_once_applicative(0)
        };
    }

    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        if EVALUATION_ORDER == Normal {
            let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
            assert_eq!(beta_full(should_reduce), Var(1))
        }
    }

    #[test]
    fn applicative_order() {
        if EVALUATION_ORDER == Applicative {
            let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
            assert_eq!(beta_once(shouldnt_reduce), parse(&"(λ2)((λ111)(λ111)(λ111))").unwrap())
        }
    }
}
<commit_msg>adjust some docs and tests<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction%20strategies) of
/// β-reductions. `Applicative` order will not fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator. The default is `Normal`.
pub const EVALUATION_ORDER: Order = Normal;

/// The available variants of β-reduction evaluation order.
#[derive(Debug, PartialEq)]
pub enum Order {
    Normal,
    Applicative
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVALUATION_ORDER {
            Normal => self._beta_once_normal(0),
            Applicative => self._beta_once_applicative(0)
        };
    }

    // the return value indicates if reduction was performed
    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    // the return value indicates if reduction was performed
    fn _beta_once_applicative(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
            App(_, _) => {
                if !self.lhs_ref().unwrap().is_beta_reducible() &&
                   !self.rhs_ref().unwrap().is_beta_reducible() &&
                    self.lhs_ref().unwrap().unabs_ref().is_ok()
                {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Checks whether `self` is β-reducible.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::{succ, zero};
    ///
    /// let reducible     = succ().app(1.into());
    /// let not_reducible = zero();
    ///
    /// assert!(reducible.is_beta_reducible());
    /// assert!(!not_reducible.is_beta_reducible());
    /// ```
    pub fn is_beta_reducible(&self) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    true
                } else {
                    self.lhs_ref().unwrap().is_beta_reducible() ||
                    self.rhs_ref().unwrap().is_beta_reducible()
                }
            }
        }
    }

    /// Performs full β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn evaluation_order() {
        match EVALUATION_ORDER {
            Normal => {
                let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(beta_full(should_reduce), Var(1))
            },
            Applicative => {
                let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
                assert_eq!(beta_once(shouldnt_reduce), parse(&"(λ2)((λ111)(λ111)(λ111))").unwrap())
            }
        }
    }
}
<|endoftext|>


<commit_before>use core::ops;
use vmx;
use cpu;
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

lazy_static! {
    static ref CLI_COUNT: cpu::PerCoreVariable<AtomicUsize> = Default::default();
}

pub fn are_interrupts_enabled() -> bool {
    (vmx::read_flags() & 0x200) != 0
}

pub fn cli() {
    unsafe {
        asm!("cli" : : :);
    }
}

pub fn sti() {
    unsafe {
        asm!("sti" : : :);
    }
}

#[derive(Default)]
pub struct ClearLocalInterruptsGuard<T> {
    guarded: T,
    count: AtomicUsize,
}

impl<T> ClearLocalInterruptsGuard<T> {
    pub fn new(guarded: T) -> Self {
        ClearLocalInterruptsGuard { guarded: guarded, count: ATOMIC_USIZE_INIT }
    }
}

impl<T> ops::Deref for ClearLocalInterruptsGuard<T> {
    type Target = T;
    fn deref(&self) -> &T {
        cli();
        if self.count.fetch_add(1, Ordering::AcqRel) == 0 {
            CLI_COUNT.get().fetch_add(1, Ordering::AcqRel);
        }
        &self.guarded
    }
}

impl<T> ops::DerefMut for ClearLocalInterruptsGuard<T> {
    fn deref_mut(&mut self) -> &mut T {
        cli();
        if self.count.fetch_add(1, Ordering::AcqRel) == 0 {
            CLI_COUNT.get().fetch_add(1, Ordering::AcqRel);
        }
        &mut self.guarded
    }
}

impl<T> Drop for ClearLocalInterruptsGuard<T> {
    fn drop(&mut self) {
        if self.count.fetch_sub(1, Ordering::AcqRel) == 1 && CLI_COUNT.get().fetch_sub(1, Ordering::AcqRel) == 1 {
                sti();
        }
    }
}
<commit_msg>Cargo fmt<commit_after>use core::ops;
use vmx;
use cpu;
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

lazy_static! {
    static ref CLI_COUNT: cpu::PerCoreVariable<AtomicUsize> = Default::default();
}

pub fn are_interrupts_enabled() -> bool {
    (vmx::read_flags() & 0x200) != 0
}

pub fn cli() {
    unsafe {
        asm!("cli" : : :);
    }
}

pub fn sti() {
    unsafe {
        asm!("sti" : : :);
    }
}

#[derive(Default)]
pub struct ClearLocalInterruptsGuard<T> {
    guarded: T,
    count: AtomicUsize,
}

impl<T> ClearLocalInterruptsGuard<T> {
    pub fn new(guarded: T) -> Self {
        ClearLocalInterruptsGuard {
            guarded: guarded,
            count: ATOMIC_USIZE_INIT,
        }
    }
}

impl<T> ops::Deref for ClearLocalInterruptsGuard<T> {
    type Target = T;
    fn deref(&self) -> &T {
        cli();
        if self.count.fetch_add(1, Ordering::AcqRel) == 0 {
            CLI_COUNT.get().fetch_add(1, Ordering::AcqRel);
        }
        &self.guarded
    }
}

impl<T> ops::DerefMut for ClearLocalInterruptsGuard<T> {
    fn deref_mut(&mut self) -> &mut T {
        cli();
        if self.count.fetch_add(1, Ordering::AcqRel) == 0 {
            CLI_COUNT.get().fetch_add(1, Ordering::AcqRel);
        }
        &mut self.guarded
    }
}

impl<T> Drop for ClearLocalInterruptsGuard<T> {
    fn drop(&mut self) {
        if self.count.fetch_sub(1, Ordering::AcqRel) == 1 &&
           CLI_COUNT.get().fetch_sub(1, Ordering::AcqRel) == 1 {
            sti();
        }
    }
}
<|endoftext|>