<commit_before>//! Client Connection Pooling
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::net::{SocketAddr, Shutdown};
use std::sync::{Arc, Mutex};

use std::time::Duration;

use net::{NetworkConnector, NetworkStream, DefaultConnector};

/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
pub struct Pool<C: NetworkConnector> {
    connector: C,
    inner: Arc<Mutex<PoolImpl<<C as NetworkConnector>::Stream>>>
}

/// Config options for the `Pool`.
#[derive(Debug)]
pub struct Config {
    /// The maximum idle connections *per host*.
    pub max_idle: usize,
}

impl Default for Config {
    #[inline]
    fn default() -> Config {
        Config {
            max_idle: 5,
        }
    }
}

#[derive(Debug)]
struct PoolImpl<S> {
    conns: HashMap<Key, Vec<PooledStreamInner<S>>>,
    config: Config,
}

type Key = (String, u16, Scheme);

fn key<T: Into<Scheme>>(host: &str, port: u16, scheme: T) -> Key {
    (host.to_owned(), port, scheme.into())
}

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
enum Scheme {
    Http,
    Https,
    Other(String)
}

impl<'a> From<&'a str> for Scheme {
    fn from(s: &'a str) -> Scheme {
        match s {
            "http" => Scheme::Http,
            "https" => Scheme::Https,
            s => Scheme::Other(String::from(s))
        }
    }
}

impl Pool<DefaultConnector> {
    /// Creates a `Pool` with a `DefaultConnector`.
    #[inline]
    pub fn new(config: Config) -> Pool<DefaultConnector> {
        Pool::with_connector(config, DefaultConnector::default())
    }
}

impl<C: NetworkConnector> Pool<C> {
    /// Creates a `Pool` with a specified `NetworkConnector`.
    #[inline]
    pub fn with_connector(config: Config, connector: C) -> Pool<C> {
        Pool {
            connector: connector,
            inner: Arc::new(Mutex::new(PoolImpl {
                conns: HashMap::new(),
                config: config,
            }))
        }
    }

    /// Clear all idle connections from the Pool, closing them.
    #[inline]
    pub fn clear_idle(&mut self) {
        self.inner.lock().unwrap().conns.clear();
    }
}

impl<S> PoolImpl<S> {
    fn reuse(&mut self, key: Key, conn: PooledStreamInner<S>) {
        trace!("reuse {:?}", key);
        let conns = self.conns.entry(key).or_insert(vec![]);
        if conns.len() < self.config.max_idle {
            conns.push(conn);
        }
    }
}

impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector for Pool<C> {
    type Stream = PooledStream<S>;
    fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<PooledStream<S>> {
        let key = key(host, port, scheme);
        let mut locked = self.inner.lock().unwrap();
        let mut should_remove = false;
        let inner = match locked.conns.get_mut(&key) {
            Some(ref mut vec) => {
                trace!("Pool had connection, using");
                should_remove = vec.len() == 1;
                vec.pop().unwrap()
            }
            _ => PooledStreamInner {
                key: key.clone(),
                stream: try!(self.connector.connect(host, port, scheme)),
                previous_response_expected_no_content: false,
            }
        };
        if should_remove {
            locked.conns.remove(&key);
        }
        Ok(PooledStream {
            inner: Some(inner),
            is_closed: false,
            pool: self.inner.clone(),
        })
    }
}

/// A Stream that will try to be returned to the Pool when dropped.
pub struct PooledStream<S> {
    inner: Option<PooledStreamInner<S>>,
    is_closed: bool,
    pool: Arc<Mutex<PoolImpl<S>>>,
}

#[derive(Debug)]
struct PooledStreamInner<S> {
    key: Key,
    stream: S,
    previous_response_expected_no_content: bool,
}

impl<S: NetworkStream> Read for PooledStream<S> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.inner.as_mut().unwrap().stream.read(buf) {
            Ok(0) => {
                // if the wrapped stream returns EOF (Ok(0)), that means the
                // server has closed the stream. we must be sure this stream
                // is dropped and not put back into the pool.
                self.is_closed = true;
                Ok(0)
            },
            r => r
        }
    }
}

impl<S: NetworkStream> Write for PooledStream<S> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.as_mut().unwrap().stream.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.inner.as_mut().unwrap().stream.flush()
    }
}

impl<S: NetworkStream> NetworkStream for PooledStream<S> {
    #[inline]
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        self.inner.as_mut().unwrap().stream.peer_addr()
    }

    #[inline]
    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.inner.as_ref().unwrap().stream.set_read_timeout(dur)
    }

    #[inline]
    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.inner.as_ref().unwrap().stream.set_write_timeout(dur)
    }

    #[inline]
    fn close(&mut self, how: Shutdown) -> io::Result<()> {
        self.is_closed = true;
        self.inner.as_mut().unwrap().stream.close(how)
    }

    #[inline]
    fn set_previous_response_expected_no_content(&mut self, expected: bool) {
        trace!("set_previous_response_expected_no_content {}", expected);
        self.inner.as_mut().unwrap().previous_response_expected_no_content = expected;
    }

    #[inline]
    fn previous_response_expected_no_content(&self) -> bool {
        let answer = self.inner.as_ref().unwrap().previous_response_expected_no_content;
        trace!("previous_response_expected_no_content {}", answer);
        answer
    }
}

impl<S> Drop for PooledStream<S> {
    fn drop(&mut self) {
        trace!("PooledStream.drop, is_closed={}", self.is_closed);
        if !self.is_closed {
            self.inner.take().map(|inner| {
                if let Ok(mut pool) = self.pool.lock() {
                    pool.reuse(inner.key.clone(), inner);
                }
                // else poisoned, give up
            });
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::Shutdown;
    use std::io::Read;
    use mock::{MockConnector};
    use net::{NetworkConnector, NetworkStream};

    use super::{Pool, key};

    macro_rules! mocked {
        () => ({
            Pool::with_connector(Default::default(), MockConnector)
        })
    }

    #[test]
    fn test_connect_and_drop() {
        let pool = mocked!();
        let key = key("127.0.0.1", 3000, "http");
        pool.connect("127.0.0.1", 3000, "http").unwrap();
        {
            let locked = pool.inner.lock().unwrap();
            assert_eq!(locked.conns.len(), 1);
            assert_eq!(locked.conns.get(&key).unwrap().len(), 1);
        }
        pool.connect("127.0.0.1", 3000, "http").unwrap(); //reused
        {
            let locked = pool.inner.lock().unwrap();
            assert_eq!(locked.conns.len(), 1);
            assert_eq!(locked.conns.get(&key).unwrap().len(), 1);
        }
    }

    #[test]
    fn test_closed() {
        let pool = mocked!();
        let mut stream = pool.connect("127.0.0.1", 3000, "http").unwrap();
        stream.close(Shutdown::Both).unwrap();
        drop(stream);
        let locked = pool.inner.lock().unwrap();
        assert_eq!(locked.conns.len(), 0);
    }

    #[test]
    fn test_eof_closes() {
        let pool = mocked!();

        let mut stream = pool.connect("127.0.0.1", 3000, "http").unwrap();
        assert_eq!(stream.read(&mut [0]).unwrap(), 0);
        drop(stream);
        let locked = pool.inner.lock().unwrap();
        assert_eq!(locked.conns.len(), 0);
    }
}
<commit_msg>perf(client): dont keep Pool mutex locked during getaddrinfo<commit_after>//! Client Connection Pooling
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::net::{SocketAddr, Shutdown};
use std::sync::{Arc, Mutex};

use std::time::Duration;

use net::{NetworkConnector, NetworkStream, DefaultConnector};

/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
pub struct Pool<C: NetworkConnector> {
    connector: C,
    inner: Arc<Mutex<PoolImpl<<C as NetworkConnector>::Stream>>>
}

/// Config options for the `Pool`.
#[derive(Debug)]
pub struct Config {
    /// The maximum idle connections *per host*.
    pub max_idle: usize,
}

impl Default for Config {
    #[inline]
    fn default() -> Config {
        Config {
            max_idle: 5,
        }
    }
}

#[derive(Debug)]
struct PoolImpl<S> {
    conns: HashMap<Key, Vec<PooledStreamInner<S>>>,
    config: Config,
}

type Key = (String, u16, Scheme);

fn key<T: Into<Scheme>>(host: &str, port: u16, scheme: T) -> Key {
    (host.to_owned(), port, scheme.into())
}

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
enum Scheme {
    Http,
    Https,
    Other(String)
}

impl<'a> From<&'a str> for Scheme {
    fn from(s: &'a str) -> Scheme {
        match s {
            "http" => Scheme::Http,
            "https" => Scheme::Https,
            s => Scheme::Other(String::from(s))
        }
    }
}

impl Pool<DefaultConnector> {
    /// Creates a `Pool` with a `DefaultConnector`.
    #[inline]
    pub fn new(config: Config) -> Pool<DefaultConnector> {
        Pool::with_connector(config, DefaultConnector::default())
    }
}

impl<C: NetworkConnector> Pool<C> {
    /// Creates a `Pool` with a specified `NetworkConnector`.
    #[inline]
    pub fn with_connector(config: Config, connector: C) -> Pool<C> {
        Pool {
            connector: connector,
            inner: Arc::new(Mutex::new(PoolImpl {
                conns: HashMap::new(),
                config: config,
            }))
        }
    }

    /// Clear all idle connections from the Pool, closing them.
    #[inline]
    pub fn clear_idle(&mut self) {
        self.inner.lock().unwrap().conns.clear();
    }
}

impl<S> PoolImpl<S> {
    fn reuse(&mut self, key: Key, conn: PooledStreamInner<S>) {
        trace!("reuse {:?}", key);
        let conns = self.conns.entry(key).or_insert(vec![]);
        if conns.len() < self.config.max_idle {
            conns.push(conn);
        }
    }
}

impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector for Pool<C> {
    type Stream = PooledStream<S>;
    fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<PooledStream<S>> {
        let key = key(host, port, scheme);
        let mut should_remove = false;
        let inner = match self.inner.lock().unwrap().conns.get_mut(&key) {
            Some(ref mut vec) => {
                trace!("Pool had connection, using");
                should_remove = vec.len() == 1;
                vec.pop().unwrap()
            }
            None => PooledStreamInner {
                key: key.clone(),
                stream: try!(self.connector.connect(host, port, scheme)),
                previous_response_expected_no_content: false,
            }
        };
        if should_remove {
            self.inner.lock().unwrap().conns.remove(&key);
        }
        Ok(PooledStream {
            inner: Some(inner),
            is_closed: false,
            pool: self.inner.clone(),
        })
    }
}

/// A Stream that will try to be returned to the Pool when dropped.
pub struct PooledStream<S> {
    inner: Option<PooledStreamInner<S>>,
    is_closed: bool,
    pool: Arc<Mutex<PoolImpl<S>>>,
}

#[derive(Debug)]
struct PooledStreamInner<S> {
    key: Key,
    stream: S,
    previous_response_expected_no_content: bool,
}

impl<S: NetworkStream> Read for PooledStream<S> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.inner.as_mut().unwrap().stream.read(buf) {
            Ok(0) => {
                // if the wrapped stream returns EOF (Ok(0)), that means the
                // server has closed the stream. we must be sure this stream
                // is dropped and not put back into the pool.
                self.is_closed = true;
                Ok(0)
            },
            r => r
        }
    }
}

impl<S: NetworkStream> Write for PooledStream<S> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.as_mut().unwrap().stream.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.inner.as_mut().unwrap().stream.flush()
    }
}

impl<S: NetworkStream> NetworkStream for PooledStream<S> {
    #[inline]
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        self.inner.as_mut().unwrap().stream.peer_addr()
    }

    #[inline]
    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.inner.as_ref().unwrap().stream.set_read_timeout(dur)
    }

    #[inline]
    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.inner.as_ref().unwrap().stream.set_write_timeout(dur)
    }

    #[inline]
    fn close(&mut self, how: Shutdown) -> io::Result<()> {
        self.is_closed = true;
        self.inner.as_mut().unwrap().stream.close(how)
    }

    #[inline]
    fn set_previous_response_expected_no_content(&mut self, expected: bool) {
        trace!("set_previous_response_expected_no_content {}", expected);
        self.inner.as_mut().unwrap().previous_response_expected_no_content = expected;
    }

    #[inline]
    fn previous_response_expected_no_content(&self) -> bool {
        let answer = self.inner.as_ref().unwrap().previous_response_expected_no_content;
        trace!("previous_response_expected_no_content {}", answer);
        answer
    }
}

impl<S> Drop for PooledStream<S> {
    fn drop(&mut self) {
        trace!("PooledStream.drop, is_closed={}", self.is_closed);
        if !self.is_closed {
            self.inner.take().map(|inner| {
                if let Ok(mut pool) = self.pool.lock() {
                    pool.reuse(inner.key.clone(), inner);
                }
                // else poisoned, give up
            });
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::Shutdown;
    use std::io::Read;
    use mock::{MockConnector};
    use net::{NetworkConnector, NetworkStream};

    use super::{Pool, key};

    macro_rules! mocked {
        () => ({
            Pool::with_connector(Default::default(), MockConnector)
        })
    }

    #[test]
    fn test_connect_and_drop() {
        let pool = mocked!();
        let key = key("127.0.0.1", 3000, "http");
        pool.connect("127.0.0.1", 3000, "http").unwrap();
        {
            let locked = pool.inner.lock().unwrap();
            assert_eq!(locked.conns.len(), 1);
            assert_eq!(locked.conns.get(&key).unwrap().len(), 1);
        }
        pool.connect("127.0.0.1", 3000, "http").unwrap(); //reused
        {
            let locked = pool.inner.lock().unwrap();
            assert_eq!(locked.conns.len(), 1);
            assert_eq!(locked.conns.get(&key).unwrap().len(), 1);
        }
    }

    #[test]
    fn test_closed() {
        let pool = mocked!();
        let mut stream = pool.connect("127.0.0.1", 3000, "http").unwrap();
        stream.close(Shutdown::Both).unwrap();
        drop(stream);
        let locked = pool.inner.lock().unwrap();
        assert_eq!(locked.conns.len(), 0);
    }

    #[test]
    fn test_eof_closes() {
        let pool = mocked!();

        let mut stream = pool.connect("127.0.0.1", 3000, "http").unwrap();
        assert_eq!(stream.read(&mut [0]).unwrap(), 0);
        drop(stream);
        let locked = pool.inner.lock().unwrap();
        assert_eq!(locked.conns.len(), 0);
    }
}
<|endoftext|>


<commit_before>use crate::{
    ast::{Ast, Ast::*},
    ast_walk::{LazyWalkReses, WalkRule::*},
    core_forms::outermost_form,
    core_type_forms::get__primitive_type,
    grammar::FormPat,
    name::*,
    runtime::eval::Value,
    util::assoc::Assoc,
};
use std::rc::Rc;

fn extend__capture_language(
    pc: crate::earley::ParseContext,
    _starter_info: Ast,
) -> crate::earley::ParseContext {
    use crate::runtime::reify::Reifiable;
    let reified_language = pc.reify();
    crate::earley::ParseContext {
        grammar: assoc_n!("OnlyNt" =>
            Rc::new(FormPat::Named(n("body"), Rc::new(FormPat::Anyways(Node(
                basic_typed_form!(
                    [], // No syntax
                    cust_rc_box!(|parts| {
                        // Reify the current type environment:
                        let mut struct_body = vec![];

                        for (k, v) in parts.env.iter_pairs() {
                            struct_body.push(crate::util::mbe::EnvMBE::new_from_leaves(assoc_n!(
                                "component_name" => Atom(*k),
                                "component" => v.clone()
                            )))
                        }

                        Ok(ast!({"Type" "tuple" :
                            "component" => [
                                (, get__primitive_type(n("LanguageSyntax"))),
                                (, Node(crate::core_forms::find("Type", "struct"),
                                     crate::util::mbe::EnvMBE::new_from_anon_repeat(struct_body),
                                     crate::beta::ExportBeta::Nothing))]
                    }))}),
                    cust_rc_box!(move |parts| {
                        Ok(Value::Sequence(vec![
                            // The captured reified language syntax:
                            Rc::new(reified_language.clone()),
                            // Reifying the value environment is easy:
                            Rc::new(Value::Struct(parts.env))
                        ]))})
                ),
            crate::util::mbe::EnvMBE::<Ast>::new(),
            crate::beta::ExportBeta::Nothing
        )))))),
        // We can't just squirrel `reified_language` here:
        //  these only affect earlier phases, and we need the language in phase 0
        eval_ctxt: LazyWalkReses::<crate::runtime::eval::Eval>::new_empty(),
        type_ctxt: LazyWalkReses::<crate::ty::SynthTy>::new_empty(),
    }
}

/// Run the file (which hopefully evaluates to `capture_language`), and get the language it defines.
pub fn language_from_file(
    path: &std::path::Path,
) -> (crate::earley::ParseContext, Assoc<Name, Ast>, Assoc<Name, Value>) {
    let mut raw_lib = String::new();

    use std::io::Read;
    std::fs::File::open(path)
        .expect("Error opening file")
        .read_to_string(&mut raw_lib)
        .expect("Error reading file");

    let orig_pc = crate::core_forms::outermost__parse_context();

    // TODO: I guess syntax extensions ought to return `Result`, too...
    let lib_ast = crate::grammar::parse(&outermost_form(), orig_pc.clone(), &raw_lib).unwrap();
    // TODO: This gets roundtripped (LazyWalkReses -> Assoc -> LazyWalkReses). Just call `walk`?
    let lib_typed = crate::ty::synth_type(&lib_ast, orig_pc.type_ctxt.env).unwrap();
    let lib_evaled = crate::runtime::eval::eval(&lib_ast, orig_pc.eval_ctxt.env).unwrap();
    let (new_pc, new__value_env) = if let Value::Sequence(ref lang_and_env) = lib_evaled {
        use crate::runtime::reify::Reifiable;
        let new_pc = crate::earley::ParseContext::reflect(&(*lang_and_env[0]));
        let new__value_env = if let Value::Struct(ref env) = *lang_and_env[1] {
            let mut new__value_env = Assoc::new();
            // We need to un-freshen the names that we're importing
            //  so they can actually be referred to.
            for (k, v) in env.iter_pairs() {
                new__value_env = new__value_env.set(k.unhygienic_orig(), v.clone())
            }
            new__value_env
        } else {
            icp!("[type error] Unexpected lib syntax structure: {:#?}", *lang_and_env[1])
        };
        (new_pc, new__value_env)
    } else {
        icp!("[type error] Unexpected lib syntax strucutre: {:#?}", lib_evaled);
    };

    node_let!(lib_typed => {Type tuple}
        lang_and_types *= component);
    node_let!(lang_and_types[1] => {Type struct}
        keys *= component_name, values *= component);

    let mut new__type_env = Assoc::<Name, Ast>::new();
    for (k, v) in keys.into_iter().zip(values.into_iter()) {
        // As above, unfreshen:
        new__type_env = new__type_env.set(k.to_name().unhygienic_orig(), v.clone());
    }

    (new_pc, new__type_env, new__value_env)
}

// Shift the parser into the language specified in "filename".
// TODO: This is probably unhygenic in some sense. Perhaps this needs to be a new kind of `Beta`?
fn extend_import(
    _pc: crate::earley::ParseContext,
    starter_info: Ast,
) -> crate::earley::ParseContext {
    let filename = match starter_info {
        // Skip "import" and the separator:
        Shape(ref parts) => match parts[2] {
            IncompleteNode(ref parts) => {
                parts.get_leaf_or_panic(&n("filename")).to_name().orig_sp()
            }
            _ => icp!("Unexpected structure {:#?}", parts),
        },
        _ => icp!("Unexpected structure {:#?}", starter_info),
    };

    let (new_pc, new__type_env, new__value_env) =
        language_from_file(&std::path::Path::new(&filename));

    crate::earley::ParseContext {
        grammar: new_pc.grammar.set(
            n("ImportStarter"),
            Rc::new(FormPat::Scope(
                basic_typed_form!(
                    (named "body", (call "Expr")),
                    cust_rc_box!(move |parts| {
                        parts.with_environment(
                            parts.env.set_assoc(&new__type_env)).get_res(n("body"))
                    }),
                    cust_rc_box!(move |parts| {
                        parts.with_environment(
                            parts.env.set_assoc(&new__value_env)).get_res(n("body"))
                    })
                ),
                crate::beta::ExportBeta::Nothing,
            )),
        ),
        ..new_pc
    }
}

/// Some of these forms are theoretically implementable as macros from other forms,
///  but for performance and debugability reasons, they are a part of Unseemly.
/// Other of these forms are just not central to the design of Unseemly and have ad-hoc designs.
///
/// Stored as a `FormPat` instead of a `SynEnv`
///  because we need to merge this with the rest of the "Expr"s.
pub fn make_core_extra_forms() -> FormPat {
    // I think we want to have "Stmt" separate from "Expr", once #4 is complete.
    // Should "Item"s be valid "Stmt"s? Let's do whatever Rust does.

    forms_to_form_pat![
        typed_form!("block",
            (delim "-{", "{", [(star [(named "effect", (call "Expr")), (lit ";")]),
                            (named "result", (call "Expr"))]),
            /* type */
            Body(n("result")),
            /* evaluation */
            cust_rc_box!( move | part_values | {
                for effect_values in part_values.march_all(&[n("effect")]) {
                    let _ = effect_values.get_res(n("effect"))?;
                }
                part_values.get_res(n("result"))
        })),
        typed_form!("capture_language",
            // Immediately descend into a grammar with one NT pointing to one form,
            //  which has captured the whole parse context.
            (extend_nt [(lit "capture_language")], "OnlyNt", extend__capture_language),
            Body(n("body")),
            Body(n("body"))),
        typed_form!("import_language_from_file",
            (extend
                [(lit "import"), (call "DefaultSeparator"),
                    (named "filename", (scan r"/\[(.*)]/"))],
                (named "body", (call "ImportStarter")),
                extend_import),
            Body(n("body")),
            Body(n("body"))),
        typed_form!("string_literal",
            (named "body", (scan r#"\s*"((?:[^"\\]|\\"|\\\\)*)""#)),
            cust_rc_box!(|_| {
                Ok(ast!({"Type" "String" :}))
            }),
            cust_rc_box!(|parts| {
                // Undo the escaping:
                Ok(Value::Text(parts.get_term(n("body")).to_name().orig_sp()
                    .replace(r#"\""#, r#"""#)
                    .replace(r#"\\"#, r#"\"#)))
            })
        )
    ]
}
<commit_msg>Add `prefab_type`, for turning the type `T` into a value `Type<T>`.<commit_after>use crate::{
    ast::{Ast, Ast::*},
    ast_walk::{LazyWalkReses, WalkRule::*},
    core_forms::outermost_form,
    core_type_forms::get__primitive_type,
    grammar::FormPat,
    name::*,
    runtime::eval::Value,
    util::assoc::Assoc,
};
use std::rc::Rc;

fn extend__capture_language(
    pc: crate::earley::ParseContext,
    _starter_info: Ast,
) -> crate::earley::ParseContext {
    use crate::runtime::reify::Reifiable;
    let reified_language = pc.reify();
    crate::earley::ParseContext {
        grammar: assoc_n!("OnlyNt" =>
            Rc::new(FormPat::Named(n("body"), Rc::new(FormPat::Anyways(Node(
                basic_typed_form!(
                    [], // No syntax
                    cust_rc_box!(|parts| {
                        // Reify the current type environment:
                        let mut struct_body = vec![];

                        for (k, v) in parts.env.iter_pairs() {
                            struct_body.push(crate::util::mbe::EnvMBE::new_from_leaves(assoc_n!(
                                "component_name" => Atom(*k),
                                "component" => v.clone()
                            )))
                        }

                        Ok(ast!({"Type" "tuple" :
                            "component" => [
                                (, get__primitive_type(n("LanguageSyntax"))),
                                (, Node(crate::core_forms::find("Type", "struct"),
                                     crate::util::mbe::EnvMBE::new_from_anon_repeat(struct_body),
                                     crate::beta::ExportBeta::Nothing))]
                    }))}),
                    cust_rc_box!(move |parts| {
                        Ok(Value::Sequence(vec![
                            // The captured reified language syntax:
                            Rc::new(reified_language.clone()),
                            // Reifying the value environment is easy:
                            Rc::new(Value::Struct(parts.env))
                        ]))})
                ),
            crate::util::mbe::EnvMBE::<Ast>::new(),
            crate::beta::ExportBeta::Nothing
        )))))),
        // We can't just squirrel `reified_language` here:
        //  these only affect earlier phases, and we need the language in phase 0
        eval_ctxt: LazyWalkReses::<crate::runtime::eval::Eval>::new_empty(),
        type_ctxt: LazyWalkReses::<crate::ty::SynthTy>::new_empty(),
    }
}

/// Run the file (which hopefully evaluates to `capture_language`), and get the language it defines.
pub fn language_from_file(
    path: &std::path::Path,
) -> (crate::earley::ParseContext, Assoc<Name, Ast>, Assoc<Name, Value>) {
    let mut raw_lib = String::new();

    use std::io::Read;
    std::fs::File::open(path)
        .expect("Error opening file")
        .read_to_string(&mut raw_lib)
        .expect("Error reading file");

    let orig_pc = crate::core_forms::outermost__parse_context();

    // TODO: I guess syntax extensions ought to return `Result`, too...
    let lib_ast = crate::grammar::parse(&outermost_form(), orig_pc.clone(), &raw_lib).unwrap();
    // TODO: This gets roundtripped (LazyWalkReses -> Assoc -> LazyWalkReses). Just call `walk`?
    let lib_typed = crate::ty::synth_type(&lib_ast, orig_pc.type_ctxt.env).unwrap();
    let lib_evaled = crate::runtime::eval::eval(&lib_ast, orig_pc.eval_ctxt.env).unwrap();
    let (new_pc, new__value_env) = if let Value::Sequence(ref lang_and_env) = lib_evaled {
        use crate::runtime::reify::Reifiable;
        let new_pc = crate::earley::ParseContext::reflect(&(*lang_and_env[0]));
        let new__value_env = if let Value::Struct(ref env) = *lang_and_env[1] {
            let mut new__value_env = Assoc::new();
            // We need to un-freshen the names that we're importing
            //  so they can actually be referred to.
            for (k, v) in env.iter_pairs() {
                new__value_env = new__value_env.set(k.unhygienic_orig(), v.clone())
            }
            new__value_env
        } else {
            icp!("[type error] Unexpected lib syntax structure: {:#?}", *lang_and_env[1])
        };
        (new_pc, new__value_env)
    } else {
        icp!("[type error] Unexpected lib syntax strucutre: {:#?}", lib_evaled);
    };

    node_let!(lib_typed => {Type tuple}
        lang_and_types *= component);
    node_let!(lang_and_types[1] => {Type struct}
        keys *= component_name, values *= component);

    let mut new__type_env = Assoc::<Name, Ast>::new();
    for (k, v) in keys.into_iter().zip(values.into_iter()) {
        // As above, unfreshen:
        new__type_env = new__type_env.set(k.to_name().unhygienic_orig(), v.clone());
    }

    (new_pc, new__type_env, new__value_env)
}

// Shift the parser into the language specified in "filename".
// TODO: This is probably unhygenic in some sense. Perhaps this needs to be a new kind of `Beta`?
fn extend_import(
    _pc: crate::earley::ParseContext,
    starter_info: Ast,
) -> crate::earley::ParseContext {
    let filename = match starter_info {
        // Skip "import" and the separator:
        Shape(ref parts) => match parts[2] {
            IncompleteNode(ref parts) => {
                parts.get_leaf_or_panic(&n("filename")).to_name().orig_sp()
            }
            _ => icp!("Unexpected structure {:#?}", parts),
        },
        _ => icp!("Unexpected structure {:#?}", starter_info),
    };

    let (new_pc, new__type_env, new__value_env) =
        language_from_file(&std::path::Path::new(&filename));

    crate::earley::ParseContext {
        grammar: new_pc.grammar.set(
            n("ImportStarter"),
            Rc::new(FormPat::Scope(
                basic_typed_form!(
                    (named "body", (call "Expr")),
                    cust_rc_box!(move |parts| {
                        parts.with_environment(
                            parts.env.set_assoc(&new__type_env)).get_res(n("body"))
                    }),
                    cust_rc_box!(move |parts| {
                        parts.with_environment(
                            parts.env.set_assoc(&new__value_env)).get_res(n("body"))
                    })
                ),
                crate::beta::ExportBeta::Nothing,
            )),
        ),
        ..new_pc
    }
}

/// Some of these forms are theoretically implementable as macros from other forms,
///  but for performance and debugability reasons, they are a part of Unseemly.
/// Other of these forms are just not central to the design of Unseemly and have ad-hoc designs.
///
/// Stored as a `FormPat` instead of a `SynEnv`
///  because we need to merge this with the rest of the "Expr"s.
pub fn make_core_extra_forms() -> FormPat {
    // I think we want to have "Stmt" separate from "Expr", once #4 is complete.
    // Should "Item"s be valid "Stmt"s? Let's do whatever Rust does.

    forms_to_form_pat![
        typed_form!("prefab_type",
            [(lit "prefab_type"), (named "ty", (call "Type"))],
            /* type */
            cust_rc_box!(move |part_types| {
                Ok(ast!({"Type" "type_apply" :
                    "type_rator" => (, (get__primitive_type(n("Type")))),
                    "arg" => [(, part_types.get_res(n("ty"))?)]
                }))
            }),
            /* evaluation */
            // HACK: at evaluation time, nobody cares
            cust_rc_box!(move |_| {
                Ok(Value::AbstractSyntax(ast!((trivial))))
            })
        ),
        typed_form!("block",
            (delim "-{", "{", [(star [(named "effect", (call "Expr")), (lit ";")]),
                            (named "result", (call "Expr"))]),
            /* type */
            Body(n("result")),
            /* evaluation */
            cust_rc_box!( move | part_values | {
                for effect_values in part_values.march_all(&[n("effect")]) {
                    let _ = effect_values.get_res(n("effect"))?;
                }
                part_values.get_res(n("result"))
        })),
        typed_form!("capture_language",
            // Immediately descend into a grammar with one NT pointing to one form,
            //  which has captured the whole parse context.
            (extend_nt [(lit "capture_language")], "OnlyNt", extend__capture_language),
            Body(n("body")),
            Body(n("body"))),
        typed_form!("import_language_from_file",
            (extend
                [(lit "import"), (call "DefaultSeparator"),
                    (named "filename", (scan r"/\[(.*)]/"))],
                (named "body", (call "ImportStarter")),
                extend_import),
            Body(n("body")),
            Body(n("body"))),
        typed_form!("string_literal",
            (named "body", (scan r#"\s*"((?:[^"\\]|\\"|\\\\)*)""#)),
            cust_rc_box!(|_| {
                Ok(ast!({"Type" "String" :}))
            }),
            cust_rc_box!(|parts| {
                // Undo the escaping:
                Ok(Value::Text(parts.get_term(n("body")).to_name().orig_sp()
                    .replace(r#"\""#, r#"""#)
                    .replace(r#"\\"#, r#"\"#)))
            })
        )
    ]
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {

	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        let new_cpu_state = self.execute(cpu_state,mem);
        // Perform logging, recording here...
        *cpu_state = new_cpu_state
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);
        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( ((mem.read8(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536 ) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap())
            },
            AddressMode::IndirectIndexed => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
            },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( ((mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) % 256) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( ((mem.read8(cpu_state.pc+1).unwrap() + cpu_state.y) % 256) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) -> CpuState {

    	// The state of the CPU after executing the current instruction.
    	let mut new_cpu_state = cpu_state.clone();

    	// Figure out which opcode is being executed.
    	let ref decode_register = cpu_state.decode_register;

    	match decode_register.info.opcode_class {

    		// ASL (Arithmetic Shift Left)
    		OpcodeClass::ASL => {

				// Shift accumulator to the left.
				new_cpu_state.a = cpu_state.a << 1;
				// Carry flag gets set to the most significant bit.
				new_cpu_state.C = (128 & cpu_state.a) > 0;
				// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// LDA (Load Accumulator)
    		OpcodeClass::LDA => {
    			new_cpu_state.a = decode_register.value_final.unwrap()
    		},

    		// LDX (Load X)
    		OpcodeClass::LDX => {
    			new_cpu_state.x = decode_register.value_final.unwrap()
    		},

    		// LDY (Load Y)
    		OpcodeClass::LDY => {
    			new_cpu_state.y = decode_register.value_final.unwrap()
    		},

    		// LSR (Logical Shift Right)
    		OpcodeClass::LSR => {

    			// Shift accumulator to the right.
    			new_cpu_state.a = cpu_state.a >> 1;
    			// Carry flag gets set to the least significant bit.
    			new_cpu_state.C = 1 & cpu_state.a > 0;
    			// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// NOP (No Operation)
    		OpcodeClass::NOP => {},

    		// Default: not sure what this opcode is.
			_ => panic!("Unrecognised opcode class: {:?}", decode_register.info.opcode_class)

    	}

    	// Expression returned is the updated cpu_state.
    	new_cpu_state
    }

}

<commit_msg>AbsoluteX/Y are now wrapping properly.fixed idx wrapping in other funcs<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {

	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        let new_cpu_state = self.execute(cpu_state,mem);
        // Perform logging, recording here...
        *cpu_state = new_cpu_state
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);
        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( (mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16) % 65535);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( (mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16) % 65535);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( ((mem.read8(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65535 ) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap())
            },
            AddressMode::IndirectIndexed => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65535) as u16);
            },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( ((mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) % 255) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( ((mem.read8(cpu_state.pc+1).unwrap() + cpu_state.y) % 255) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) -> CpuState {

    	// The state of the CPU after executing the current instruction.
    	let mut new_cpu_state = cpu_state.clone();

    	// Figure out which opcode is being executed.
    	let ref decode_register = cpu_state.decode_register;

    	match decode_register.info.opcode_class {

    		// ASL (Arithmetic Shift Left)
    		OpcodeClass::ASL => {

				// Shift accumulator to the left.
				new_cpu_state.a = cpu_state.a << 1;
				// Carry flag gets set to the most significant bit.
				new_cpu_state.C = (128 & cpu_state.a) > 0;
				// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// LDA (Load Accumulator)
    		OpcodeClass::LDA => {
    			new_cpu_state.a = decode_register.value_final.unwrap()
    		},

    		// LDX (Load X)
    		OpcodeClass::LDX => {
    			new_cpu_state.x = decode_register.value_final.unwrap()
    		},

    		// LDY (Load Y)
    		OpcodeClass::LDY => {
    			new_cpu_state.y = decode_register.value_final.unwrap()
    		},

    		// LSR (Logical Shift Right)
    		OpcodeClass::LSR => {

    			// Shift accumulator to the right.
    			new_cpu_state.a = cpu_state.a >> 1;
    			// Carry flag gets set to the least significant bit.
    			new_cpu_state.C = 1 & cpu_state.a > 0;
    			// Set zero flag if result is 0.
    			if new_cpu_state.a == 0 { new_cpu_state.Z = true }

    		},

    		// NOP (No Operation)
    		OpcodeClass::NOP => {},

    		// Default: not sure what this opcode is.
			_ => panic!("Unrecognised opcode class: {:?}", decode_register.info.opcode_class)

    	}

    	// Expression returned is the updated cpu_state.
    	new_cpu_state
    }

}

<|endoftext|>


<commit_before>mod changelog;

use xshell::{cmd, Shell};

use crate::{date_iso, flags, is_release_tag, project_root};

impl flags::Release {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        if !self.dry_run {
            cmd!(sh, "git switch release").run()?;
            cmd!(sh, "git fetch upstream --tags --force").run()?;
            cmd!(sh, "git reset --hard tags/nightly").run()?;
            // The `release` branch sometimes has a couple of cherry-picked
            // commits for patch releases. If that's the case, just overwrite
            // it. As we are setting `release` branch to an up-to-date `nightly`
            // tag, this shouldn't be problematic in general.
            //
            // Note that, as we tag releases, we don't worry about "losing"
            // commits -- they'll be kept alive by the tag. More generally, we
            // don't care about historic releases all that much, it's fine even
            // to delete old tags.
            cmd!(sh, "git push --force").run()?;
        }

        // Generates bits of manual.adoc.
        cmd!(sh, "cargo test -p ide_assists -p ide_diagnostics -p rust-analyzer -- sourcegen_")
            .run()?;

        let website_root = project_root().join("../rust-analyzer.github.io");
        {
            let _dir = sh.push_dir(&website_root);
            cmd!(sh, "git switch src").run()?;
            cmd!(sh, "git pull").run()?;
        }
        let changelog_dir = website_root.join("./thisweek/_posts");

        let today = date_iso(sh)?;
        let commit = cmd!(sh, "git rev-parse HEAD").read()?;
        let changelog_n = sh.read_dir(changelog_dir.as_path())?.len();

        for adoc in [
            "manual.adoc",
            "generated_assists.adoc",
            "generated_config.adoc",
            "generated_diagnostic.adoc",
            "generated_features.adoc",
        ] {
            let src = project_root().join("./docs/user/").join(adoc);
            let dst = website_root.join(adoc);

            let contents = sh.read_file(src)?;
            sh.write_file(dst, contents)?;
        }

        let tags = cmd!(sh, "git tag --list").read()?;
        let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();

        let contents = changelog::get_changelog(sh, changelog_n, &commit, prev_tag, &today)?;
        let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
        sh.write_file(&path, &contents)?;

        Ok(())
    }
}

impl flags::Promote {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        let _dir = sh.push_dir("../rust-rust-analyzer");
        cmd!(sh, "git switch master").run()?;
        cmd!(sh, "git fetch upstream").run()?;
        cmd!(sh, "git reset --hard upstream/master").run()?;
        cmd!(sh, "git submodule update --recursive").run()?;

        let date = date_iso(sh)?;
        let branch = format!("rust-analyzer-{date}");
        cmd!(sh, "git switch -c {branch}").run()?;
        {
            let _dir = sh.push_dir("src/tools/rust-analyzer");
            cmd!(sh, "git fetch origin").run()?;
            cmd!(sh, "git reset --hard origin/release").run()?;
        }
        cmd!(sh, "git add src/tools/rust-analyzer").run()?;
        cmd!(sh, "git commit -m':arrow_up: rust-analyzer'").run()?;
        if !self.dry_run {
            cmd!(sh, "git push -u origin {branch}").run()?;
            cmd!(
                sh,
                "xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost"
            )
            .run()?;
        }
        Ok(())
    }
}
<commit_msg>Auto merge of #12133 - lnicola:crate-dashes, r=lnicola<commit_after>mod changelog;

use xshell::{cmd, Shell};

use crate::{date_iso, flags, is_release_tag, project_root};

impl flags::Release {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        if !self.dry_run {
            cmd!(sh, "git switch release").run()?;
            cmd!(sh, "git fetch upstream --tags --force").run()?;
            cmd!(sh, "git reset --hard tags/nightly").run()?;
            // The `release` branch sometimes has a couple of cherry-picked
            // commits for patch releases. If that's the case, just overwrite
            // it. As we are setting `release` branch to an up-to-date `nightly`
            // tag, this shouldn't be problematic in general.
            //
            // Note that, as we tag releases, we don't worry about "losing"
            // commits -- they'll be kept alive by the tag. More generally, we
            // don't care about historic releases all that much, it's fine even
            // to delete old tags.
            cmd!(sh, "git push --force").run()?;
        }

        // Generates bits of manual.adoc.
        cmd!(sh, "cargo test -p ide-assists -p ide-diagnostics -p rust-analyzer -- sourcegen_")
            .run()?;

        let website_root = project_root().join("../rust-analyzer.github.io");
        {
            let _dir = sh.push_dir(&website_root);
            cmd!(sh, "git switch src").run()?;
            cmd!(sh, "git pull").run()?;
        }
        let changelog_dir = website_root.join("./thisweek/_posts");

        let today = date_iso(sh)?;
        let commit = cmd!(sh, "git rev-parse HEAD").read()?;
        let changelog_n = sh.read_dir(changelog_dir.as_path())?.len();

        for adoc in [
            "manual.adoc",
            "generated_assists.adoc",
            "generated_config.adoc",
            "generated_diagnostic.adoc",
            "generated_features.adoc",
        ] {
            let src = project_root().join("./docs/user/").join(adoc);
            let dst = website_root.join(adoc);

            let contents = sh.read_file(src)?;
            sh.write_file(dst, contents)?;
        }

        let tags = cmd!(sh, "git tag --list").read()?;
        let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();

        let contents = changelog::get_changelog(sh, changelog_n, &commit, prev_tag, &today)?;
        let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
        sh.write_file(&path, &contents)?;

        Ok(())
    }
}

impl flags::Promote {
    pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
        let _dir = sh.push_dir("../rust-rust-analyzer");
        cmd!(sh, "git switch master").run()?;
        cmd!(sh, "git fetch upstream").run()?;
        cmd!(sh, "git reset --hard upstream/master").run()?;
        cmd!(sh, "git submodule update --recursive").run()?;

        let date = date_iso(sh)?;
        let branch = format!("rust-analyzer-{date}");
        cmd!(sh, "git switch -c {branch}").run()?;
        {
            let _dir = sh.push_dir("src/tools/rust-analyzer");
            cmd!(sh, "git fetch origin").run()?;
            cmd!(sh, "git reset --hard origin/release").run()?;
        }
        cmd!(sh, "git add src/tools/rust-analyzer").run()?;
        cmd!(sh, "git commit -m':arrow_up: rust-analyzer'").run()?;
        if !self.dry_run {
            cmd!(sh, "git push -u origin {branch}").run()?;
            cmd!(
                sh,
                "xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost"
            )
            .run()?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use parse::Node;
use evaluator::Evaluator;
use directives::{consume_string, DirectiveHandler};

pub struct If;

impl DirectiveHandler for If {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        let mut iter = args.iter();
        let condition = consume_string(&mut iter, evaluator).ok_or(())?;
        let if_true = iter.next().ok_or(())?;
        let if_false = iter.next();

        if iter.next().is_some() {
            return Err(());
        }

        if condition.is_empty() {
            match if_false {
                Some(expr) => Ok(evaluator.evaluate(expr)),
                None => Ok("".to_owned()),
            }
        } else {
            Ok(evaluator.evaluate(if_true))
        }
    }
}

pub struct Not;

impl DirectiveHandler for Not {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        if args.len() != 1 {
            return Err(());
        }

        let mut iter = args.iter();
        let value = consume_string(&mut iter, evaluator).ok_or(())?;

        if value.is_empty() {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

pub struct Equals;

impl DirectiveHandler for Equals {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        if args.len() < 2 {
            return Err(());
        }

        let mut iter = args.iter();
        let initial = consume_string(&mut iter, evaluator).ok_or(())?;

        let is_true = iter.all(|node| initial == evaluator.evaluate(node));

        if is_true {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

pub struct NotEquals;

impl DirectiveHandler for NotEquals {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        let equals = Equals;
        let result = equals.handle(evaluator, args)?;

        if result.is_empty() {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

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

    fn node_string(s: &str) -> Node {
        Node::new_string(s, 0, -1)
    }

    fn node_children(nodes: Vec<Node>) -> Node {
        Node::new_children(nodes, 0, -1)
    }

    #[test]
    fn test_if() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(directives::Concat));
        let handler = If;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("true")])
                .is_err()
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[node_string(""), node_string("true"), node_string("false")]
            ),
            Ok("false".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foobar")]),
                    node_string("true"),
                    node_string("false")
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("")]),
                    node_string("true"),
                    node_string("false")
                ]
            ),
            Ok("false".to_owned())
        );
    }

    #[test]
    fn test_not() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(directives::Concat));
        let handler = Not;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo"), node_string("bar")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo")]),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("")]),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[node_children(vec![node_string("concat"), node_string("")])]
            ),
            Ok("true".to_owned())
        );
    }

    #[test]
    fn test_equals() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(directives::Concat));
        let handler = Equals;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("foo")]),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")]),
                    node_string("foo")
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_string("foo"),
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("bar")]),
            Ok("".to_owned())
        );
    }

    #[test]
    fn test_not_equals() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(directives::Concat));
        let handler = NotEquals;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("foo")]),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")]),
                    node_string("foo")
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_string("foo"),
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("bar")]),
            Ok("true".to_owned())
        );
    }
}
<commit_msg>Fix tests<commit_after>use parse::Node;
use evaluator::Evaluator;
use directives::{consume_string, DirectiveHandler};

pub struct If;

impl DirectiveHandler for If {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        let mut iter = args.iter();
        let condition = consume_string(&mut iter, evaluator).ok_or(())?;
        let if_true = iter.next().ok_or(())?;
        let if_false = iter.next();

        if iter.next().is_some() {
            return Err(());
        }

        if condition.is_empty() {
            match if_false {
                Some(expr) => Ok(evaluator.evaluate(expr)),
                None => Ok("".to_owned()),
            }
        } else {
            Ok(evaluator.evaluate(if_true))
        }
    }
}

pub struct Not;

impl DirectiveHandler for Not {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        if args.len() != 1 {
            return Err(());
        }

        let mut iter = args.iter();
        let value = consume_string(&mut iter, evaluator).ok_or(())?;

        if value.is_empty() {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

pub struct Equals;

impl DirectiveHandler for Equals {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        if args.len() < 2 {
            return Err(());
        }

        let mut iter = args.iter();
        let initial = consume_string(&mut iter, evaluator).ok_or(())?;

        let is_true = iter.all(|node| initial == evaluator.evaluate(node));

        if is_true {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

pub struct NotEquals;

impl DirectiveHandler for NotEquals {
    fn handle(&self, evaluator: &mut Evaluator, args: &[Node]) -> Result<String, ()> {
        let equals = Equals;
        let result = equals.handle(evaluator, args)?;

        if result.is_empty() {
            Ok("true".to_owned())
        } else {
            Ok("".to_owned())
        }
    }
}

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

    fn node_string(s: &str) -> Node {
        Node::new_string(s, 0, -1)
    }

    fn node_children(nodes: Vec<Node>) -> Node {
        Node::new_children(nodes, 0, -1)
    }

    #[test]
    fn test_if() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(Concat));
        let handler = If;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("true")])
                .is_err()
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[node_string(""), node_string("true"), node_string("false")]
            ),
            Ok("false".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foobar")]),
                    node_string("true"),
                    node_string("false")
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("")]),
                    node_string("true"),
                    node_string("false")
                ]
            ),
            Ok("false".to_owned())
        );
    }

    #[test]
    fn test_not() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(Concat));
        let handler = Not;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo"), node_string("bar")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo")]),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("")]),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[node_children(vec![node_string("concat"), node_string("")])]
            ),
            Ok("true".to_owned())
        );
    }

    #[test]
    fn test_equals() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(Concat));
        let handler = Equals;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("foo")]),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")]),
                    node_string("foo")
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_string("foo"),
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("true".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("bar")]),
            Ok("".to_owned())
        );
    }

    #[test]
    fn test_not_equals() {
        let mut evaluator = Evaluator::new();
        evaluator.register("concat", Box::new(Concat));
        let handler = NotEquals;

        assert!(handler.handle(&mut evaluator, &[]).is_err());
        assert!(
            handler
                .handle(&mut evaluator, &[node_string("foo")])
                .is_err()
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("foo")]),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_children(vec![node_string("concat"), node_string("foo")]),
                    node_string("foo")
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(
                &mut evaluator,
                &[
                    node_string("foo"),
                    node_children(vec![node_string("concat"), node_string("foo")])
                ]
            ),
            Ok("".to_owned())
        );
        assert_eq!(
            handler.handle(&mut evaluator, &[node_string("foo"), node_string("bar")]),
            Ok("true".to_owned())
        );
    }
}
<|endoftext|>


<commit_before>use url;
use std::net;
use plugin::{Extensible, Pluggable};
pub use iron::{Url, Handler};

use iron::{self};
use bodyparser;

use backend::{self};
use super::super::framework;

use server::method;
use server::header;
use super::super::errors;
use super::request;

pub type HandleResultStrict<T> = Result<T, errors::StrictErrorResponse>;
pub type HandleResult<T> = Result<T, errors::ErrorResponse>;
pub type HandleSuccessResult = HandleResult<()>;

impl<'a> request::Body for iron::request::Body<'a> { }

pub trait WrapUrl {
    fn wrap_url(self) -> Url;
}

impl WrapUrl for url::Url {
    fn wrap_url(self) -> Url {
        Url::from_generic_url(self).unwrap()
    }
}

impl backend::AsUrl for Url {
    fn scheme(&self) -> &str { self.scheme.as_slice() }
    fn host(&self) -> &url::Host { &self.host }
    fn port(&self) -> &u16 { &self.port }
    fn path(&self) -> &Vec<String> { &self.path }
    fn username(&self) -> &Option<String> { &self.username }
    fn password(&self) -> &Option<String> { &self.password }
    fn query(&self) -> &Option<String> { &self.query }
    fn fragment(&self) -> &Option<String> { &self.fragment }
}

impl<'a> backend::Request for iron::Request<'a> {
    fn remote_addr(&self) -> &net::SocketAddr { &self.remote_addr }
    fn headers(&self) -> &header::Headers { &self.headers }
    fn method(&self) -> &method::Method { &self.method }
    fn url(&self) -> &backend::AsUrl { &self.url }
    fn body(&self) -> &request::Body { &self.body }
    fn body_mut(&mut self) -> &mut request::Body { &mut self.body }
    fn read_to_end(&mut self) -> Result<Option<String>, Box<errors::Error>> {
        self.get::<bodyparser::Raw>().map_err(|err| Box::new(err) as Box<errors::Error>)
    }
}

impl<'a>  ::Extensible for iron::Request<'a> {
    fn ext(&self) -> &::typemap::TypeMap { self.extensions() }
    fn ext_mut(&mut self) -> &mut ::typemap::TypeMap { self.extensions_mut() }
}

impl Handler for framework::Application {
    fn handle<'a>(&self, req: &mut iron::Request<'a>) -> iron::IronResult<iron::Response> {
        self.call(req)
            .map(|resp| {
                iron::Response {
                    status: Some(resp.status),
                    headers: resp.headers,
                    body: resp.body,
                    extensions: resp.ext
                }
            })
            .map_err(|err_resp| {
                let errors::StrictErrorResponse{error, response} = err_resp;
                iron::IronError {
                    error: error,
                    response: iron::Response {
                        status: Some(response.status),
                        headers: response.headers,
                        body: response.body,
                        extensions: response.ext
                    }
                }
            })
    }
}
<commit_msg>refactor(lib): update to the latest iron<commit_after>use url;
use std::net;
use plugin::{Extensible, Pluggable};
pub use iron::{Url, Handler};

use iron::{self};
use bodyparser;

use backend::{self};
use super::super::framework;

use server::method;
use server::header;
use super::super::errors;
use super::request;

pub type HandleResultStrict<T> = Result<T, errors::StrictErrorResponse>;
pub type HandleResult<T> = Result<T, errors::ErrorResponse>;
pub type HandleSuccessResult = HandleResult<()>;

impl<'a, 'b> request::Body for iron::request::Body<'a, 'b> { }

pub trait WrapUrl {
    fn wrap_url(self) -> Url;
}

impl WrapUrl for url::Url {
    fn wrap_url(self) -> Url {
        Url::from_generic_url(self).unwrap()
    }
}

impl backend::AsUrl for Url {
    fn scheme(&self) -> &str { self.scheme.as_slice() }
    fn host(&self) -> &url::Host { &self.host }
    fn port(&self) -> &u16 { &self.port }
    fn path(&self) -> &Vec<String> { &self.path }
    fn username(&self) -> &Option<String> { &self.username }
    fn password(&self) -> &Option<String> { &self.password }
    fn query(&self) -> &Option<String> { &self.query }
    fn fragment(&self) -> &Option<String> { &self.fragment }
}

impl<'a, 'b> backend::Request for iron::Request<'a, 'b> {
    fn remote_addr(&self) -> &net::SocketAddr { &self.remote_addr }
    fn headers(&self) -> &header::Headers { &self.headers }
    fn method(&self) -> &method::Method { &self.method }
    fn url(&self) -> &backend::AsUrl { &self.url }
    fn body(&self) -> &request::Body { &self.body }
    fn body_mut(&mut self) -> &mut request::Body { &mut self.body }
    fn read_to_end(&mut self) -> Result<Option<String>, Box<errors::Error>> {
        self.get::<bodyparser::Raw>().map_err(|err| Box::new(err) as Box<errors::Error>)
    }
}

impl<'a, 'b>  ::Extensible for iron::Request<'a, 'b> {
    fn ext(&self) -> &::typemap::TypeMap { self.extensions() }
    fn ext_mut(&mut self) -> &mut ::typemap::TypeMap { self.extensions_mut() }
}

impl Handler for framework::Application {
    fn handle<'a, 'b>(&self, req: &mut iron::Request<'a, 'b>) -> iron::IronResult<iron::Response> {
        self.call(req)
            .map(|resp| {
                iron::Response {
                    status: Some(resp.status),
                    headers: resp.headers,
                    body: resp.body,
                    extensions: resp.ext
                }
            })
            .map_err(|err_resp| {
                let errors::StrictErrorResponse{error, response} = err_resp;
                iron::IronError {
                    error: error,
                    response: iron::Response {
                        status: Some(response.status),
                        headers: response.headers,
                        body: response.body,
                        extensions: response.ext
                    }
                }
            })
    }
}
<|endoftext|>


<commit_before>use libc::c_long;

use std::ffi::CString;
use std::str;

use nix::errno::Errno::*;
use nix::Error::Sys;
use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive};
use nix::mqueue::{MqAttr, MQ_OFlag};
use nix::sys::stat::Mode;

#[test]
fn test_mq_send_and_receive() {
    const MSG_SIZE: c_long =  32;
    let attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();

    let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
    if let Err(Sys(ENOSYS)) = r0 {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd0 = r0.unwrap();
    let msg_to_send = "msg_1";
    mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap();

    let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
    let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap();
    let mut buf = [0u8; 32];
    let mut prio = 0u32;
    let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap();
    assert!(prio == 1);

    mq_close(mqd1).unwrap();
    mq_close(mqd0).unwrap();
    assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
}

#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_mq_getattr() {
    use nix::mqueue::mq_getattr;
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let read_attr = mq_getattr(mqd);
    assert!(read_attr.unwrap() == initial_attr);
    mq_close(mqd).unwrap();
}

// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
fn test_mq_setattr() {
    use nix::mqueue::{mq_getattr, mq_setattr};
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let new_attr =  MqAttr::new(0, 20, MSG_SIZE * 2, 100);
    let old_attr = mq_setattr(mqd, &new_attr);
    assert!(old_attr.unwrap() == initial_attr);

    let new_attr_get = mq_getattr(mqd);
    // The following tests make sense. No changes here because according to the Linux man page only
    // O_NONBLOCK can be set (see tests below)
    assert!(new_attr_get.unwrap() != new_attr);

    let new_attr_non_blocking =  MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0);
    mq_setattr(mqd, &new_attr_non_blocking).unwrap();
    let new_attr_get = mq_getattr(mqd);

    // now the O_NONBLOCK flag has been set
    assert!(new_attr_get.unwrap() != initial_attr);
    assert!(new_attr_get.unwrap() == new_attr_non_blocking);
    mq_close(mqd).unwrap();
}

// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
fn test_mq_set_nonblocking() {
    use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock};
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();
    mq_set_nonblock(mqd).unwrap();
    let new_attr = mq_getattr(mqd);
    assert!(new_attr.unwrap().flags() == MQ_OFlag::O_NONBLOCK.bits() as c_long);
    mq_remove_nonblock(mqd).unwrap();
    let new_attr = mq_getattr(mqd);
    assert!(new_attr.unwrap().flags() == 0);
    mq_close(mqd).unwrap();
}

#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_mq_unlink() {
    use nix::mqueue::mq_unlink;
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
    let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let res_unlink = mq_unlink(mq_name_opened);
    assert!(res_unlink == Ok(()) );

    let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
    assert!(res_unlink_not_opened == Err(Sys(ENOENT)) );

    mq_close(mqd).unwrap();
    let res_unlink_after_close = mq_unlink(mq_name_opened);
    assert!(res_unlink_after_close == Err(Sys(ENOENT)) );
}
<commit_msg>Improve debugging of mq tests by using assert_* macros<commit_after>use libc::c_long;

use std::ffi::CString;
use std::str;

use nix::errno::Errno::*;
use nix::Error::Sys;
use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive};
use nix::mqueue::{MqAttr, MQ_OFlag};
use nix::sys::stat::Mode;

#[test]
fn test_mq_send_and_receive() {
    const MSG_SIZE: c_long =  32;
    let attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();

    let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
    if let Err(Sys(ENOSYS)) = r0 {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd0 = r0.unwrap();
    let msg_to_send = "msg_1";
    mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap();

    let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
    let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap();
    let mut buf = [0u8; 32];
    let mut prio = 0u32;
    let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap();
    assert!(prio == 1);

    mq_close(mqd1).unwrap();
    mq_close(mqd0).unwrap();
    assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
}

#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_mq_getattr() {
    use nix::mqueue::mq_getattr;
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let read_attr = mq_getattr(mqd).unwrap();
    assert_eq!(read_attr, initial_attr);
    mq_close(mqd).unwrap();
}

// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
fn test_mq_setattr() {
    use nix::mqueue::{mq_getattr, mq_setattr};
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let new_attr =  MqAttr::new(0, 20, MSG_SIZE * 2, 100);
    let old_attr = mq_setattr(mqd, &new_attr).unwrap();
    assert_eq!(old_attr, initial_attr);

    let new_attr_get = mq_getattr(mqd).unwrap();
    // The following tests make sense. No changes here because according to the Linux man page only
    // O_NONBLOCK can be set (see tests below)
    assert_ne!(new_attr_get, new_attr);

    let new_attr_non_blocking =  MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0);
    mq_setattr(mqd, &new_attr_non_blocking).unwrap();
    let new_attr_get = mq_getattr(mqd).unwrap();

    // now the O_NONBLOCK flag has been set
    assert_ne!(new_attr_get, initial_attr);
    assert_eq!(new_attr_get, new_attr_non_blocking);
    mq_close(mqd).unwrap();
}

// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
fn test_mq_set_nonblocking() {
    use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock};
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();
    mq_set_nonblock(mqd).unwrap();
    let new_attr = mq_getattr(mqd);
    assert!(new_attr.unwrap().flags() == MQ_OFlag::O_NONBLOCK.bits() as c_long);
    mq_remove_nonblock(mqd).unwrap();
    let new_attr = mq_getattr(mqd);
    assert!(new_attr.unwrap().flags() == 0);
    mq_close(mqd).unwrap();
}

#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_mq_unlink() {
    use nix::mqueue::mq_unlink;
    const MSG_SIZE: c_long =  32;
    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
    let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
    let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
    let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
    let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
    let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
    if let Err(Sys(ENOSYS)) = r {
        println!("message queues not supported or module not loaded?");
        return;
    };
    let mqd = r.unwrap();

    let res_unlink = mq_unlink(mq_name_opened);
    assert!(res_unlink == Ok(()) );

    let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
    assert!(res_unlink_not_opened == Err(Sys(ENOENT)) );

    mq_close(mqd).unwrap();
    let res_unlink_after_close = mq_unlink(mq_name_opened);
    assert!(res_unlink_after_close == Err(Sys(ENOENT)) );
}
<|endoftext|>