<commit_before>/// Represents a position within a substrate.
/// Each position coordinate is mapped to an input of a CPPN.
pub trait Position {
    fn coords(&self) -> &[f64];
    fn distance(&self, other: &Self) -> f64;
    fn relative_position(&self, other: &Self) -> Self;

    fn origin() -> Self;

    /// Linearly interpolates between `self` and `other`.
    /// For t = 0.0 this should return `self`. For t = 1.0 this
    /// should return `other`.
    fn interpolate(&self, other: &Self, t: f64) -> Self;

    fn interpolate_multi(&self, other: &Self, t: &Self) -> Self;
}

pub struct Position2d([f64; 2]);

impl Position2d {
    #[inline(always)]
    pub fn new(x: f64, y: f64) -> Position2d {
        Position2d([x, y])
    }

    #[inline(always)]
    pub fn x(&self) -> f64 {
        self.0[0]
    }

    #[inline(always)]
    pub fn y(&self) -> f64 {
        self.0[1]
    }

    #[inline(always)]
    pub fn xy(&self) -> (f64, f64) {
        (self.0[0], self.0[1])
    }

}

impl Position for Position2d {
    #[inline(always)]
    fn coords(&self) -> &[f64] {
        &self.0
    }

    #[inline(always)]
    fn origin() -> Self {
        Position2d::new(0.0, 0.0)
    }

    #[inline]
    fn distance(&self, other: &Self) -> f64 {
        ((self.x() - other.x()).powi(2) + (self.y() - other.y()).powi(2)).sqrt()
    }

    #[inline]
    fn relative_position(&self, other: &Self) -> Self {
        Position2d::new(self.x() - other.x(), self.y() - other.y())
    }

    fn interpolate(&self, other: &Self, t: f64) -> Self {
        let x = self.x() * (1.0 - t) + other.x() * t;
        let y = self.y() * (1.0 - t) + other.y() * t;
        Position2d([x, y])
    }

    fn interpolate_multi(&self, other: &Self, t: &Self) -> Self {
        let tx = t.x();
        let ty = t.y();
        let x = self.x() * (1.0 - tx) + other.x() * tx;
        let y = self.y() * (1.0 - ty) + other.y() * ty;
        Position2d([x, y])
    }
}

#[test]
fn test_interpolate_one_axis() {
    let a = Position2d::new(-1.0, 0.0);
    let b = Position2d::new(1.0, 0.0);

    assert_eq!((-1.0, 0.0), a.interpolate(&b, 0.0).xy());
    assert_eq!((0.0, 0.0),  a.interpolate(&b, 0.5).xy());
    assert_eq!((1.0, 0.0),  a.interpolate(&b, 1.0).xy());
}

#[test]
fn test_interpolate_two_axes() {
    let a = Position2d::new(-1.0, 1.0);
    let b = Position2d::new(1.0, -1.0);

    assert_eq!((-1.0, 1.0), a.interpolate(&b, 0.0).xy());
    assert_eq!((0.0, 0.0),  a.interpolate(&b, 0.5).xy());
    assert_eq!((1.0, -1.0),  a.interpolate(&b, 1.0).xy());
}

#[test]
fn test_interpolate_multi() {
    let a = Position2d::new(-1.0, 1.0);
    let b = Position2d::new(1.0, -1.0);

    assert_eq!((-1.0, -1.0), a.interpolate_multi(&b, &Position2d::new(0.0, 1.0)).xy());
    assert_eq!((0.0, 0.0),  a.interpolate_multi(&b, &Position2d::new(0.5, 0.5)).xy());
    assert_eq!((1.0, 1.0),  a.interpolate_multi(&b, &Position2d::new(1.0, 0.0)).xy());
}
<commit_msg>Separate Interpolate trait from Position<commit_after>/// Represents a position within a substrate.
/// Each position coordinate is mapped to an input of a CPPN.
pub trait Position {
    fn coords(&self) -> &[f64];
    fn distance(&self, other: &Self) -> f64;
    fn relative_position(&self, other: &Self) -> Self;

    fn origin() -> Self;

}

pub trait Interpolate {
    /// Linearly interpolates between `self` and `other`.
    /// For t = 0.0 this should return `self`. For t = 1.0 this
    /// should return `other`.
    fn interpolate(&self, other: &Self, t: f64) -> Self;

    fn interpolate_multi(&self, other: &Self, t: &Self) -> Self;
}

pub struct Position2d([f64; 2]);

impl Position2d {
    #[inline(always)]
    pub fn new(x: f64, y: f64) -> Position2d {
        Position2d([x, y])
    }

    #[inline(always)]
    pub fn x(&self) -> f64 {
        self.0[0]
    }

    #[inline(always)]
    pub fn y(&self) -> f64 {
        self.0[1]
    }

    #[inline(always)]
    pub fn xy(&self) -> (f64, f64) {
        (self.0[0], self.0[1])
    }

}

impl Position for Position2d {
    #[inline(always)]
    fn coords(&self) -> &[f64] {
        &self.0
    }

    #[inline(always)]
    fn origin() -> Self {
        Position2d::new(0.0, 0.0)
    }

    #[inline]
    fn distance(&self, other: &Self) -> f64 {
        ((self.x() - other.x()).powi(2) + (self.y() - other.y()).powi(2)).sqrt()
    }

    #[inline]
    fn relative_position(&self, other: &Self) -> Self {
        Position2d::new(self.x() - other.x(), self.y() - other.y())
    }
}

impl Interpolate for Position2d {
    fn interpolate(&self, other: &Self, t: f64) -> Self {
        let x = self.x() * (1.0 - t) + other.x() * t;
        let y = self.y() * (1.0 - t) + other.y() * t;
        Position2d([x, y])
    }

    fn interpolate_multi(&self, other: &Self, t: &Self) -> Self {
        let tx = t.x();
        let ty = t.y();
        let x = self.x() * (1.0 - tx) + other.x() * tx;
        let y = self.y() * (1.0 - ty) + other.y() * ty;
        Position2d([x, y])
    }
}

#[test]
fn test_interpolate_one_axis() {
    let a = Position2d::new(-1.0, 0.0);
    let b = Position2d::new(1.0, 0.0);

    assert_eq!((-1.0, 0.0), a.interpolate(&b, 0.0).xy());
    assert_eq!((0.0, 0.0),  a.interpolate(&b, 0.5).xy());
    assert_eq!((1.0, 0.0),  a.interpolate(&b, 1.0).xy());
}

#[test]
fn test_interpolate_two_axes() {
    let a = Position2d::new(-1.0, 1.0);
    let b = Position2d::new(1.0, -1.0);

    assert_eq!((-1.0, 1.0), a.interpolate(&b, 0.0).xy());
    assert_eq!((0.0, 0.0),  a.interpolate(&b, 0.5).xy());
    assert_eq!((1.0, -1.0),  a.interpolate(&b, 1.0).xy());
}

#[test]
fn test_interpolate_multi() {
    let a = Position2d::new(-1.0, 1.0);
    let b = Position2d::new(1.0, -1.0);

    assert_eq!((-1.0, -1.0), a.interpolate_multi(&b, &Position2d::new(0.0, 1.0)).xy());
    assert_eq!((0.0, 0.0),  a.interpolate_multi(&b, &Position2d::new(0.5, 0.5)).xy());
    assert_eq!((1.0, 1.0),  a.interpolate_multi(&b, &Position2d::new(1.0, 0.0)).xy());
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::io;
use std::rc::Rc;
use std::{i32, u64};

use bytes::BytesMut;
use futures::sync::mpsc;
use futures::{self, Async, Poll, StartSend, Stream};
use tokio_core::reactor::Handle;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::multiplex::{ClientProto, RequestId};

use lber::common::TagClass;
use lber::{Consumer, ConsumerState, Input, IResult, Move};
use lber::structure::{PL, StructureTag};
use lber::structures::{ASNTag, Integer, Null, Sequence, Tag};
use lber::parse::parse_uint;
use lber::parse::Parser;
use lber::universal::Types;
use lber::write;

use controls::Control;
use controls_impl::{parse_controls, build_tag};
use exop::Exop;
use ldap::LdapOp;
use result::LdapResult;
use search::SearchItem;

pub type LdapRequestId = i32;

pub struct ProtoBundle {
    pub search_helpers: HashMap<RequestId, SearchHelper>,
    pub id_map: HashMap<LdapRequestId, RequestId>,
    pub next_id: LdapRequestId,
    pub handle: Handle,
    pub solo_ops: VecDeque<LdapRequestId>,
}

impl ProtoBundle {
    fn create_search_helper(&mut self, id: RequestId, tx: mpsc::UnboundedSender<SearchItem>) {
        self.search_helpers.insert(id, SearchHelper {
            seen: false,
            msgid: 0,           // not valid, must be properly initialized later
            tx: tx,
        });
    }

    fn inc_next_id(&mut self) -> LdapRequestId {
        if self.next_id == i32::MAX {
            self.next_id = 0;
        }
        self.next_id += 1;
        self.next_id
    }
}

pub struct SearchHelper {
    pub seen: bool,
    pub msgid: LdapRequestId,
    pub tx: mpsc::UnboundedSender<SearchItem>,
}

impl SearchHelper {
    fn send_item(&mut self, item: SearchItem) -> io::Result<()> {
        self.tx.unbounded_send(item).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
    }
}

#[derive(Clone, Debug)]
pub struct LdapResultExt(pub LdapResult, pub Exop);

impl From<Tag> for LdapResultExt {
    fn from(t: Tag) -> LdapResultExt {
        let t = match t {
            Tag::StructureTag(t) => t,
            _ => unimplemented!(),
        };
        let mut tags = t.expect_constructed().expect("result sequence").into_iter();
        let rc = match parse_uint(tags.next().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Enumerated as u64))
                .and_then(|t| t.expect_primitive()).expect("result code")
                .as_slice()) {
            IResult::Done(_, rc) => rc as u32,
            _ => panic!("failed to parse result code"),
        };
        let matched = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("matched dn");
        let text = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("diagnostic message");
        let mut refs = Vec::new();
        let mut exop_name = None;
        let mut exop_val = None;
        loop {
            match tags.next() {
                None => break,
                Some(comp) => match comp.id {
                    3 => {
                        let raw_refs = match comp.expect_constructed() {
                            Some(rr) => rr,
                            None => panic!("failed to parse referrals"),
                        };
                        refs.push(raw_refs.into_iter()
                            .map(|t| t.expect_primitive().expect("octet string"))
                            .map(String::from_utf8)
                            .map(|s| s.expect("uri"))
                            .collect());
                    },
                    10 => {
                        exop_name = Some(String::from_utf8(comp.expect_primitive().expect("octet string")).expect("exop name"));
                    }
                    11 => {
                        exop_val = Some(comp.expect_primitive().expect("octet string"));
                    }
                    _ => (),
                },
            }
        }
        LdapResultExt(
            LdapResult {
                rc: rc,
                matched: matched,
                text: text,
                refs: refs,
                ctrls: vec![],
            },
            Exop {
                name: exop_name,
                val: exop_val
            })
    }
}

pub struct LdapCodec {
    pub bundle: Rc<RefCell<ProtoBundle>>,
}

impl Decoder for LdapCodec {
    type Item = (RequestId, (Tag, Vec<Control>));
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let decoding_error = io::Error::new(io::ErrorKind::Other, "decoding error");
        let mut parser = Parser::new();
        let (amt, tag) = match *parser.handle(Input::Element(buf)) {
            ConsumerState::Continue(_) => return Ok(None),
            ConsumerState::Error(_e) => return Err(decoding_error),
            ConsumerState::Done(amt, ref tag) => (amt, tag),
        };
        let amt = match amt {
            Move::Await(_) => return Ok(None),
            Move::Seek(_) => return Err(decoding_error),
            Move::Consume(amt) => amt,
        };
        buf.split_to(amt);
        let tag = tag.clone();
        let mut tags = match tag.match_id(Types::Sequence as u64).and_then(|t| t.expect_constructed()) {
            Some(tags) => tags,
            None => return Err(decoding_error),
        };
        let maybe_controls = tags.pop().expect("element");
        let has_controls = match maybe_controls {
            StructureTag { id, class, ref payload } if class == TagClass::Context && id == 0 => match *payload {
                PL::C(_) => true,
                PL::P(_) => return Err(decoding_error),
            },
            _ => false,
        };
        let (protoop, controls) = if has_controls {
            (tags.pop().expect("element"), Some(maybe_controls))
        } else {
            (maybe_controls, None)
        };
        let controls = match controls {
            Some(controls) => parse_controls(controls),
            None => vec![],
        };
        let msgid = match parse_uint(tags.pop().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Integer as u64))
                .and_then(|t| t.expect_primitive()).expect("message id")
                .as_slice()) {
            IResult::Done(_, id) => id as i32,
            _ => return Err(decoding_error),
        };
        let id = match self.bundle.borrow().id_map.get(&msgid) {
            Some(&id) => id,
            None => {
                warn!("discarding frame with unmatched msgid: {}", msgid);
                let null_tag = Tag::Null(Null { ..Default::default() });
                return Ok(Some((u64::MAX, (null_tag, vec![]))));
            },
        };
        match protoop.id {
            op_id @ 4 | op_id @ 5 | op_id @ 19 => {
                let null_tag = Tag::Null(Null { ..Default::default() });
                let id_tag = Tag::Integer(Integer {
                    inner: id as i64,
                    .. Default::default()
                });
                let mut bundle = self.bundle.borrow_mut();
                let mut helper = match bundle.search_helpers.get_mut(&id) {
                    Some(h) => h,
                    None => return Err(io::Error::new(io::ErrorKind::Other, format!("id mismatch: {}", id))),
                };
                helper.send_item(match op_id {
                    4 => SearchItem::Entry(protoop),
                    5 => SearchItem::Done(id, Tag::StructureTag(protoop).into(), controls),
                    19 => SearchItem::Referral(protoop),
                    _ => panic!("impossible op_id"),
                })?;
                if helper.seen {
                    Ok(Some((u64::MAX, (null_tag, vec![]))))
                } else {
                    helper.seen = true;
                    Ok(Some((id, (id_tag, vec![]))))
                }
            },
            _ => {
                self.bundle.borrow_mut().id_map.remove(&msgid);
                Ok(Some((id, (Tag::StructureTag(protoop), controls))))
            },
        }
    }
}

impl Encoder for LdapCodec {
    type Item = (RequestId, (LdapOp, Box<Fn(i32)>));
    type Error = io::Error;

    fn encode(&mut self, msg: Self::Item, into: &mut BytesMut) -> io::Result<()> {
        let (id, (op, set_msgid_in_op)) = msg;
        let (tag, controls, is_solo) = match op {
            LdapOp::Solo(tag, controls) => (tag, controls, true),
            LdapOp::Single(tag, controls) => (tag, controls, false),
            LdapOp::Multi(tag, tx, controls) => {
                self.bundle.borrow_mut().create_search_helper(id, tx);
                (tag, controls, false)
            },
        };
        let outstruct = {
            // tokio-proto ids are u64, and LDAP (client) message ids are i32 > 0,
            // so we must have wraparound logic and a mapping from the latter to
            // the former
            let mut bundle = self.bundle.borrow_mut();
            let prev_ldap_id = bundle.next_id;
            let mut next_ldap_id = prev_ldap_id;
            while bundle.id_map.entry(next_ldap_id).or_insert(id) != &id {
                next_ldap_id = bundle.inc_next_id();
                assert_ne!(next_ldap_id, prev_ldap_id, "LDAP message id wraparound with no free slots");
            }
            bundle.inc_next_id();
            set_msgid_in_op(next_ldap_id);
            if is_solo {
                bundle.solo_ops.push_back(next_ldap_id);
            }
            if let Some(ref mut helper) = bundle.search_helpers.get_mut(&id) {
                helper.msgid = next_ldap_id;
            }
            let mut msg = vec![
                Tag::Integer(Integer {
                    inner: next_ldap_id as i64,
                    .. Default::default()
                }),
                tag
            ];
            if let Some(controls) = controls {
                msg.push(Tag::StructureTag(StructureTag {
                    id: 0,
                    class: TagClass::Context,
                    payload: PL::C(controls.into_iter().map(build_tag).collect())
                }));
            }
            Tag::Sequence(Sequence {
                inner: msg,
                .. Default::default()
            }).into_structure()
        };
        trace!("Sending packet: {:?}", &outstruct);
        write::encode_into(into, outstruct)?;
        Ok(())
    }
}

#[derive(Clone)]
pub struct LdapProto {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl LdapProto {
    pub fn new(handle: Handle) -> LdapProto {
        LdapProto {
            bundle: Rc::new(RefCell::new(ProtoBundle {
                search_helpers: HashMap::new(),
                id_map: HashMap::new(),
                next_id: 1,
                handle: handle,
                solo_ops: VecDeque::new(),
            }))
        }
    }

    pub fn bundle(&self) -> Rc<RefCell<ProtoBundle>> {
        self.bundle.clone()
    }
}

pub struct ResponseFilter<T> {
    pub upstream: T,
    pub bundle: Rc<RefCell<ProtoBundle>>,
}

impl<T> Stream for ResponseFilter<T>
    where T: Stream<Item=(RequestId, (Tag, Vec<Control>)), Error=io::Error>
{
    type Item = (RequestId, (Tag, Vec<Control>));
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            let maybe_msgid = self.bundle.borrow_mut().solo_ops.pop_front();
            if let Some(msgid) = maybe_msgid {
                let id = self.bundle.borrow_mut().id_map.remove(&msgid).expect("id from id_map");
                let null_tag = Tag::Null(Null { ..Default::default() });
                return Ok(Async::Ready(Some((id, (null_tag, vec![])))));
            }
            match try_ready!(self.upstream.poll()) {
                Some((id, _)) if id == u64::MAX => continue,
                msg => return Ok(Async::Ready(msg)),
            }
        }
    }
}

impl<T> futures::Sink for ResponseFilter<T>
    where T: futures::Sink<SinkItem=(RequestId, (LdapOp, Box<Fn(i32)>)), SinkError=io::Error>
{
    type SinkItem = (RequestId, (LdapOp, Box<Fn(i32)>));
    type SinkError = io::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.upstream.poll_complete()
    }
}

impl<T: AsyncRead + AsyncWrite + 'static> ClientProto<T> for LdapProto {
    type Request = (LdapOp, Box<Fn(i32)>);
    type Response = (Tag, Vec<Control>);

    type Transport = ResponseFilter<Framed<T, LdapCodec>>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        let ldapcodec = LdapCodec {
            bundle: self.bundle.clone(),
        };
        Ok(ResponseFilter {
            upstream: io.framed(ldapcodec),
            bundle: self.bundle.clone(),
        })
    }
}
<commit_msg>Remove unnecessary mut<commit_after>use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::io;
use std::rc::Rc;
use std::{i32, u64};

use bytes::BytesMut;
use futures::sync::mpsc;
use futures::{self, Async, Poll, StartSend, Stream};
use tokio_core::reactor::Handle;
use tokio_io::codec::{Decoder, Encoder, Framed};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_proto::multiplex::{ClientProto, RequestId};

use lber::common::TagClass;
use lber::{Consumer, ConsumerState, Input, IResult, Move};
use lber::structure::{PL, StructureTag};
use lber::structures::{ASNTag, Integer, Null, Sequence, Tag};
use lber::parse::parse_uint;
use lber::parse::Parser;
use lber::universal::Types;
use lber::write;

use controls::Control;
use controls_impl::{parse_controls, build_tag};
use exop::Exop;
use ldap::LdapOp;
use result::LdapResult;
use search::SearchItem;

pub type LdapRequestId = i32;

pub struct ProtoBundle {
    pub search_helpers: HashMap<RequestId, SearchHelper>,
    pub id_map: HashMap<LdapRequestId, RequestId>,
    pub next_id: LdapRequestId,
    pub handle: Handle,
    pub solo_ops: VecDeque<LdapRequestId>,
}

impl ProtoBundle {
    fn create_search_helper(&mut self, id: RequestId, tx: mpsc::UnboundedSender<SearchItem>) {
        self.search_helpers.insert(id, SearchHelper {
            seen: false,
            msgid: 0,           // not valid, must be properly initialized later
            tx: tx,
        });
    }

    fn inc_next_id(&mut self) -> LdapRequestId {
        if self.next_id == i32::MAX {
            self.next_id = 0;
        }
        self.next_id += 1;
        self.next_id
    }
}

pub struct SearchHelper {
    pub seen: bool,
    pub msgid: LdapRequestId,
    pub tx: mpsc::UnboundedSender<SearchItem>,
}

impl SearchHelper {
    fn send_item(&mut self, item: SearchItem) -> io::Result<()> {
        self.tx.unbounded_send(item).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
    }
}

#[derive(Clone, Debug)]
pub struct LdapResultExt(pub LdapResult, pub Exop);

impl From<Tag> for LdapResultExt {
    fn from(t: Tag) -> LdapResultExt {
        let t = match t {
            Tag::StructureTag(t) => t,
            _ => unimplemented!(),
        };
        let mut tags = t.expect_constructed().expect("result sequence").into_iter();
        let rc = match parse_uint(tags.next().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Enumerated as u64))
                .and_then(|t| t.expect_primitive()).expect("result code")
                .as_slice()) {
            IResult::Done(_, rc) => rc as u32,
            _ => panic!("failed to parse result code"),
        };
        let matched = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("matched dn");
        let text = String::from_utf8(tags.next().expect("element").expect_primitive().expect("octet string"))
            .expect("diagnostic message");
        let mut refs = Vec::new();
        let mut exop_name = None;
        let mut exop_val = None;
        loop {
            match tags.next() {
                None => break,
                Some(comp) => match comp.id {
                    3 => {
                        let raw_refs = match comp.expect_constructed() {
                            Some(rr) => rr,
                            None => panic!("failed to parse referrals"),
                        };
                        refs.push(raw_refs.into_iter()
                            .map(|t| t.expect_primitive().expect("octet string"))
                            .map(String::from_utf8)
                            .map(|s| s.expect("uri"))
                            .collect());
                    },
                    10 => {
                        exop_name = Some(String::from_utf8(comp.expect_primitive().expect("octet string")).expect("exop name"));
                    }
                    11 => {
                        exop_val = Some(comp.expect_primitive().expect("octet string"));
                    }
                    _ => (),
                },
            }
        }
        LdapResultExt(
            LdapResult {
                rc: rc,
                matched: matched,
                text: text,
                refs: refs,
                ctrls: vec![],
            },
            Exop {
                name: exop_name,
                val: exop_val
            })
    }
}

pub struct LdapCodec {
    pub bundle: Rc<RefCell<ProtoBundle>>,
}

impl Decoder for LdapCodec {
    type Item = (RequestId, (Tag, Vec<Control>));
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        let decoding_error = io::Error::new(io::ErrorKind::Other, "decoding error");
        let mut parser = Parser::new();
        let (amt, tag) = match *parser.handle(Input::Element(buf)) {
            ConsumerState::Continue(_) => return Ok(None),
            ConsumerState::Error(_e) => return Err(decoding_error),
            ConsumerState::Done(amt, ref tag) => (amt, tag),
        };
        let amt = match amt {
            Move::Await(_) => return Ok(None),
            Move::Seek(_) => return Err(decoding_error),
            Move::Consume(amt) => amt,
        };
        buf.split_to(amt);
        let tag = tag.clone();
        let mut tags = match tag.match_id(Types::Sequence as u64).and_then(|t| t.expect_constructed()) {
            Some(tags) => tags,
            None => return Err(decoding_error),
        };
        let maybe_controls = tags.pop().expect("element");
        let has_controls = match maybe_controls {
            StructureTag { id, class, ref payload } if class == TagClass::Context && id == 0 => match *payload {
                PL::C(_) => true,
                PL::P(_) => return Err(decoding_error),
            },
            _ => false,
        };
        let (protoop, controls) = if has_controls {
            (tags.pop().expect("element"), Some(maybe_controls))
        } else {
            (maybe_controls, None)
        };
        let controls = match controls {
            Some(controls) => parse_controls(controls),
            None => vec![],
        };
        let msgid = match parse_uint(tags.pop().expect("element")
                .match_class(TagClass::Universal)
                .and_then(|t| t.match_id(Types::Integer as u64))
                .and_then(|t| t.expect_primitive()).expect("message id")
                .as_slice()) {
            IResult::Done(_, id) => id as i32,
            _ => return Err(decoding_error),
        };
        let id = match self.bundle.borrow().id_map.get(&msgid) {
            Some(&id) => id,
            None => {
                warn!("discarding frame with unmatched msgid: {}", msgid);
                let null_tag = Tag::Null(Null { ..Default::default() });
                return Ok(Some((u64::MAX, (null_tag, vec![]))));
            },
        };
        match protoop.id {
            op_id @ 4 | op_id @ 5 | op_id @ 19 => {
                let null_tag = Tag::Null(Null { ..Default::default() });
                let id_tag = Tag::Integer(Integer {
                    inner: id as i64,
                    .. Default::default()
                });
                let mut bundle = self.bundle.borrow_mut();
                let helper = match bundle.search_helpers.get_mut(&id) {
                    Some(h) => h,
                    None => return Err(io::Error::new(io::ErrorKind::Other, format!("id mismatch: {}", id))),
                };
                helper.send_item(match op_id {
                    4 => SearchItem::Entry(protoop),
                    5 => SearchItem::Done(id, Tag::StructureTag(protoop).into(), controls),
                    19 => SearchItem::Referral(protoop),
                    _ => panic!("impossible op_id"),
                })?;
                if helper.seen {
                    Ok(Some((u64::MAX, (null_tag, vec![]))))
                } else {
                    helper.seen = true;
                    Ok(Some((id, (id_tag, vec![]))))
                }
            },
            _ => {
                self.bundle.borrow_mut().id_map.remove(&msgid);
                Ok(Some((id, (Tag::StructureTag(protoop), controls))))
            },
        }
    }
}

impl Encoder for LdapCodec {
    type Item = (RequestId, (LdapOp, Box<Fn(i32)>));
    type Error = io::Error;

    fn encode(&mut self, msg: Self::Item, into: &mut BytesMut) -> io::Result<()> {
        let (id, (op, set_msgid_in_op)) = msg;
        let (tag, controls, is_solo) = match op {
            LdapOp::Solo(tag, controls) => (tag, controls, true),
            LdapOp::Single(tag, controls) => (tag, controls, false),
            LdapOp::Multi(tag, tx, controls) => {
                self.bundle.borrow_mut().create_search_helper(id, tx);
                (tag, controls, false)
            },
        };
        let outstruct = {
            // tokio-proto ids are u64, and LDAP (client) message ids are i32 > 0,
            // so we must have wraparound logic and a mapping from the latter to
            // the former
            let mut bundle = self.bundle.borrow_mut();
            let prev_ldap_id = bundle.next_id;
            let mut next_ldap_id = prev_ldap_id;
            while bundle.id_map.entry(next_ldap_id).or_insert(id) != &id {
                next_ldap_id = bundle.inc_next_id();
                assert_ne!(next_ldap_id, prev_ldap_id, "LDAP message id wraparound with no free slots");
            }
            bundle.inc_next_id();
            set_msgid_in_op(next_ldap_id);
            if is_solo {
                bundle.solo_ops.push_back(next_ldap_id);
            }
            if let Some(ref mut helper) = bundle.search_helpers.get_mut(&id) {
                helper.msgid = next_ldap_id;
            }
            let mut msg = vec![
                Tag::Integer(Integer {
                    inner: next_ldap_id as i64,
                    .. Default::default()
                }),
                tag
            ];
            if let Some(controls) = controls {
                msg.push(Tag::StructureTag(StructureTag {
                    id: 0,
                    class: TagClass::Context,
                    payload: PL::C(controls.into_iter().map(build_tag).collect())
                }));
            }
            Tag::Sequence(Sequence {
                inner: msg,
                .. Default::default()
            }).into_structure()
        };
        trace!("Sending packet: {:?}", &outstruct);
        write::encode_into(into, outstruct)?;
        Ok(())
    }
}

#[derive(Clone)]
pub struct LdapProto {
    bundle: Rc<RefCell<ProtoBundle>>,
}

impl LdapProto {
    pub fn new(handle: Handle) -> LdapProto {
        LdapProto {
            bundle: Rc::new(RefCell::new(ProtoBundle {
                search_helpers: HashMap::new(),
                id_map: HashMap::new(),
                next_id: 1,
                handle: handle,
                solo_ops: VecDeque::new(),
            }))
        }
    }

    pub fn bundle(&self) -> Rc<RefCell<ProtoBundle>> {
        self.bundle.clone()
    }
}

pub struct ResponseFilter<T> {
    pub upstream: T,
    pub bundle: Rc<RefCell<ProtoBundle>>,
}

impl<T> Stream for ResponseFilter<T>
    where T: Stream<Item=(RequestId, (Tag, Vec<Control>)), Error=io::Error>
{
    type Item = (RequestId, (Tag, Vec<Control>));
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            let maybe_msgid = self.bundle.borrow_mut().solo_ops.pop_front();
            if let Some(msgid) = maybe_msgid {
                let id = self.bundle.borrow_mut().id_map.remove(&msgid).expect("id from id_map");
                let null_tag = Tag::Null(Null { ..Default::default() });
                return Ok(Async::Ready(Some((id, (null_tag, vec![])))));
            }
            match try_ready!(self.upstream.poll()) {
                Some((id, _)) if id == u64::MAX => continue,
                msg => return Ok(Async::Ready(msg)),
            }
        }
    }
}

impl<T> futures::Sink for ResponseFilter<T>
    where T: futures::Sink<SinkItem=(RequestId, (LdapOp, Box<Fn(i32)>)), SinkError=io::Error>
{
    type SinkItem = (RequestId, (LdapOp, Box<Fn(i32)>));
    type SinkError = io::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        self.upstream.poll_complete()
    }
}

impl<T: AsyncRead + AsyncWrite + 'static> ClientProto<T> for LdapProto {
    type Request = (LdapOp, Box<Fn(i32)>);
    type Response = (Tag, Vec<Control>);

    type Transport = ResponseFilter<Framed<T, LdapCodec>>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        let ldapcodec = LdapCodec {
            bundle: self.bundle.clone(),
        };
        Ok(ResponseFilter {
            upstream: io.framed(ldapcodec),
            bundle: self.bundle.clone(),
        })
    }
}
<|endoftext|>


<commit_before>//! way-cooler registry.

use std::collections::HashMap;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};

pub type RegKey = String;
pub type RegVal = RegistryValue;
pub type RegMap = HashMap<RegKey, RegVal>;

lazy_static! {
    /// Registry variable for the registry
    static ref REGISTRY: RwLock<RegMap> =
        RwLock::new(HashMap::new());
}

/// A value in the registry.
#[derive(Debug, Clone)]
pub enum RegistryValue {
    /// An integer value
    Integer(i32),
    /// A floating point value
    Float(f32),
    /// A boolean value
    Boolean(bool),
    /// A string value
    String(String),
    /// A list of values.
    List(Vec<RegistryValue>)
}

/// Acquires a read lock on the registry.
pub fn read_lock<'a>()
                     -> RwLockReadGuard<'a, RegMap> {
    REGISTRY.read().unwrap()
}

/// Acquires a write lock on the registry.
pub fn write_lock<'a>()
                      -> RwLockWriteGuard<'a, RegMap> {
    REGISTRY.write().unwrap()
}

/// Gets a value from the regsitry.
pub fn get(name: &RegKey) -> Option<RegVal> {
    let mut val: Option<RegistryValue>;
    {
        let reg = REGISTRY.read().unwrap();
        val = reg.get(name).cloned();
    }
    val
}

/// Gets a value from the registry, or a default
/// if the value is not found.
pub fn get_or_default(name: &RegKey, value: RegVal) -> RegVal {
    match get(name) {
        Some(value) => value,
        None => value
    }
}

<commit_msg>Add logging to registry get<commit_after>//! way-cooler registry.

use std::collections::HashMap;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};

pub type RegKey = String;
pub type RegVal = RegistryValue;
pub type RegMap = HashMap<RegKey, RegVal>;

lazy_static! {
    /// Registry variable for the registry
    static ref REGISTRY: RwLock<RegMap> =
        RwLock::new(HashMap::new());
}

/// A value in the registry.
#[derive(Debug, Clone)]
pub enum RegistryValue {
    /// An integer value
    Integer(i32),
    /// A floating point value
    Float(f32),
    /// A boolean value
    Boolean(bool),
    /// A string value
    String(String),
    /// A list of values.
    List(Vec<RegistryValue>)
}

/// Acquires a read lock on the registry.
pub fn read_lock<'a>()
                     -> RwLockReadGuard<'a, RegMap> {
    REGISTRY.read().unwrap()
}

/// Acquires a write lock on the registry.
pub fn write_lock<'a>()
                      -> RwLockWriteGuard<'a, RegMap> {
    REGISTRY.write().unwrap()
}

/// Gets a value from the regsitry.
pub fn get(name: &RegKey) -> Option<RegVal> {
    trace!("get({})...", name);
    let mut val: Option<RegistryValue>;
    {
        let reg = REGISTRY.read().unwrap();
        val = reg.get(name).cloned();
    }
    val
}

/// Gets a value from the registry, or a default
/// if the value is not found.
pub fn get_or_default(name: &RegKey, value: RegVal) -> RegVal {
    match get(name) {
        Some(value) => value,
        None => value
    }
}

<|endoftext|>


<commit_before>use bytes::{BytesMut, BufMut};
use futures::future;
use std::fmt::Write;

use ZapResult;

/// The result of an outgoing request
pub struct Response {
    headers: BytesMut,
    response: BytesMut,
    status: usize,
}

impl Response {
    // Create a new default response
    pub fn new() -> Response {
        Response {
            headers: BytesMut::new(),
            response: BytesMut::new(),
            status: 200,
        }
    }

    /// Set the status code
    pub fn status(&mut self, code: usize) -> &mut Response {
        self.status = code;
        self
    }

    /// Set a header with value
    pub fn header(&mut self, name: &str, val: &str) -> &mut Response {
        // Encode new header and write it to buffer
        push(&mut self.headers, name.as_bytes());
        push(&mut self.headers, b": ");
        push(&mut self.headers, val.as_bytes());
        push(&mut self.headers, b"\r\n");

        self
    }

    /// Set the response body
    pub fn body(&mut self, s: &str) -> &mut Response {
        self.response.write_str(s).unwrap();
        self
    }

    /// Write raw data to the body
    pub fn body_raw(&mut self, s: &[u8]) -> &mut Response {
        push(&mut self.response, s);
        self
    }

    // Finish the request
    pub fn ok(self) -> ZapResult {
        future::ok(self)
    }
}

/// Encode our response to a buffer
pub fn encode(msg: &Response, buf: &mut BytesMut) {
    // Get the content length
    let length = msg.response.len();

    // Encode the data
    push(buf, b"HTTP/1.1 ");
    push(buf, usize_to_bytes(msg.status).as_ref());
    push(buf, b"\r\nContent-Length: ");
    push(buf, usize_to_bytes(length).as_ref());
    push(buf, b"\r\n");
    push(buf, msg.headers.as_ref());

    push(buf, b"\r\n");
    push(buf, msg.response.as_ref());
}

// Push bytes to a buffer
fn push(buf: &mut BytesMut, data: &[u8]) {
    // Alloc new space
    buf.reserve(data.len());

    unsafe {
        // Put the data in the reserved space
        buf.bytes_mut()[..data.len()].copy_from_slice(data);
        buf.advance_mut(data.len());
    }
}

// Convert a usize to raw data without strings
fn usize_to_bytes(s : usize) -> BytesMut {
    // Define varibales we need
    let mut data : BytesMut = BytesMut::new();
    let mut length = s as u32;

    // Convert u16 to ASCII bytes
    let mut add_zero = false;
    for i in 1..5 {
        let base = (10u16.pow(4 - (i as u32))) as u32;

        // Calculate ascii decimal
        let c = 48 + (&length / &base) as u8;

        // Add byte
        if c == 48 && add_zero {
            data.put_u8(c);
        } else if c != 48 {
            if !add_zero {
                add_zero = true;
            }

            data.put_u8(c);
        }

        // New length
        length = (&length % &base) as u32;
    }

    return data;
}
<commit_msg>Optimized usize to bytes<commit_after>use bytes::{BytesMut, BufMut};
use futures::future;
use std::fmt::Write;

use ZapResult;

/// The result of an outgoing request
pub struct Response {
    headers: BytesMut,
    response: BytesMut,
    status: usize,
}

impl Response {
    // Create a new default response
    pub fn new() -> Response {
        Response {
            headers: BytesMut::new(),
            response: BytesMut::new(),
            status: 200,
        }
    }

    /// Set the status code
    pub fn status(&mut self, code: usize) -> &mut Response {
        self.status = code;
        self
    }

    /// Set a header with value
    pub fn header(&mut self, name: &str, val: &str) -> &mut Response {
        // Encode new header and write it to buffer
        push(&mut self.headers, name.as_bytes());
        push(&mut self.headers, b": ");
        push(&mut self.headers, val.as_bytes());
        push(&mut self.headers, b"\r\n");

        self
    }

    /// Set the response body
    pub fn body(&mut self, s: &str) -> &mut Response {
        self.response.write_str(s).unwrap();
        self
    }

    /// Write raw data to the body
    pub fn body_raw(&mut self, s: &[u8]) -> &mut Response {
        push(&mut self.response, s);
        self
    }

    // Finish the request
    pub fn ok(self) -> ZapResult {
        future::ok(self)
    }
}

/// Encode our response to a buffer
pub fn encode(msg: &Response, buf: &mut BytesMut) {
    // Get the content length
    let length = msg.response.len();

    // Encode the data
    push(buf, b"HTTP/1.1 ");
    push(buf, usize_to_bytes(msg.status).as_ref());
    push(buf, b"\r\nContent-Length: ");
    push(buf, usize_to_bytes(length).as_ref());
    push(buf, b"\r\n");
    push(buf, msg.headers.as_ref());

    push(buf, b"\r\n");
    push(buf, msg.response.as_ref());
}

// Push bytes to a buffer
fn push(buf: &mut BytesMut, data: &[u8]) {
    // Alloc new space
    buf.reserve(data.len());

    unsafe {
        // Put the data in the reserved space
        buf.bytes_mut()[..data.len()].copy_from_slice(data);
        buf.advance_mut(data.len());
    }
}

// Convert a usize to raw data without strings
fn usize_to_bytes(s : usize) -> BytesMut {
    // Define varibales we need
    let mut data : BytesMut = BytesMut::new();
    let mut length = s as u32;

    // Convert u16 to ASCII bytes
    loop {
        // Calculate ascii decimal
        let c = 48 + (&length % 10 as u32) as u8;

        // Add Char
        data.put_u8(c);

        // New length
        length = (&length / 10 as u32) as u32;

        // Is there anything left to process?
        if length == 0 {
            break;
        }
    }

    // Reverse data
    data.reverse();

    return data;
}
<|endoftext|>


<commit_before>use copy_dir::copy_dir;
use pulldown_cmark::{html, Options, Parser};
use std::fs;

mod paths;
mod printing;

fn main() {
    printing::heading("Building website.");
    clean();
    generate();
    configure();
}

fn clean() {
    printing::subheading("Cleaning build directory.");
    paths::remove_dir(&paths::build());
}

fn generate() {
    printing::subheading("Generating build directory.");
    paths::create_dir(&paths::build());
    generate_public();
    generate_home();
    generate_code();
    generate_articles();
    generate_videos();
    generate_resume();
}

fn generate_public() {
    match copy_dir(&paths::public(), &paths::build().join("public")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

fn generate_home() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("index.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_file(
        &paths::build().join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_code() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("code.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("code"));
    paths::create_file(
        &paths::build().join("code").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_articles() {
    paths::create_dir(&paths::build().join("articles"));
    match fs::read_dir(&paths::content().join("articles")) {
        Ok(markdown_files) => {
            let mut markdown_links_to_routes = Vec::new();

            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);
                paths::create_dir(&paths::build().join("articles").join(route));
                paths::create_file(
                    &paths::build()
                        .join("articles")
                        .join(route)
                        .join("index.html"),
                    &markdown_to_html(&markdown_file_contents),
                );

                let title = match markdown_file_contents.lines().next() {
                    Some(title) => &title[2..],
                    None => panic!("Cannot find a title in {}.", &route),
                };
                markdown_links_to_routes
                    .push(format!("- [{}.](/articles/{})", title, route).to_string());
            }

            let markdown_articles_index = format!(
                "# Articles\nMy posts on software development.\n{}",
                markdown_links_to_routes.join("\n")
            );

            paths::create_file(
                &paths::build().join("articles").join("index.html"),
                &markdown_to_html(&markdown_articles_index),
            );
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

fn generate_videos() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("videos.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("videos"));
    paths::create_file(
        &paths::build().join("videos").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_resume() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("resume.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("resume"));
    paths::create_file(
        &paths::build().join("resume").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn configure() {
    printing::subheading("Configuring build directory.");

    // Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
    paths::create_file(&paths::build().join("CNAME"), "trevordmiller.com");
}

fn markdown_to_html(markdown: &str) -> std::string::String {
    let css = r#"
        html {
            font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
            color: #333333;
        }
        body {
            margin: 0;
        }
        header {
            background: #f5f5f5;
            border-bottom: 1px solid #d3d3d3;
        }
        header nav {
            max-width: 80ch;
            margin: 0 auto;
        }
        header a {
            padding: 1rem;
            font-family: "Courier New", Courier, monospace;
            text-decoration: none;
            color: #333333;
            display: block;
        }
        header a:first-of-type {
            display: flex;
            align-items: center;
        }
        header a:first-of-type::before {
            content: ">";
            margin: 0 1ch 0 0;
        }
        header a:first-of-type::after {
            content: "";
            margin: 0 0 0 1ch;
            display: inline-block;
            background: #333333;
            width: 1ch;
            height: 1em;
            animation: cursor 800ms infinite;
        }
        @keyframes cursor {
            0% {
              opacity: 0;
            }
            50% {
              opacity: 1;
            }
            to {
              opacity: 0;
            }
        }
        @media screen and (min-width: 80ch) {
            header {
              position: sticky;
              top: 0;
            }
            header nav {
              display: flex;
              align-items: center;
            }
            header a:first-of-type {
              flex: 1;
            }
        }
        main {
            max-width: 80ch;
            margin: 0 auto;
            padding: 1rem;
        }
        pre code {
            overflow: auto;
            display: block;
            padding: 1rem;
            background: #f5f5f5;
            border-radius: 3px;
        }
        footer {
            max-width: 80ch;
            margin: 2rem auto 0 auto;
            padding: 2rem 1rem 1rem 1rem;
            border-top: 1px solid #d3d3d3;
        }
        label, input {
            display: block;
            font-size: 1rem;
            margin-bottom: 0.5rem;
        }
        input[type=submit] {
            padding: 0.5rem;
            background: #333333;
            color: #ffffff;
            font-size: 1rem;
            border: 0 none;
            cursor: pointer;
            border-radius: 3px;
        }
    "#;

    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!("
        <!DOCTYPE html>

                <title>trevordmiller.com</title>
                <meta name='author' content='Trevor D. Miller'>
                <meta name='description' content='Personal website.'>
                <meta charset='utf-8'>
                <meta name='viewport' content='width=device-width, initial-scale=1'>

                        trevordmiller
                        Articles
                        Videos
                        Code
                        Resume

                    {}

Get my latest content by email

I won't sell or share your email. I won't send you spam. You can unsubscribe at any time.

                    <form action='https://trevordmiller.us10.list-manage.com/subscribe/post?u=91fe993c2d93cde48679d6826&id=f7f097d693' method='post' id='mc-embedded-subscribe-form' name='mc-embedded-subscribe-form'>
                        Email address:
                        <input type='email' required value='' name='EMAIL' id='mce-EMAIL'>

<input type='text' name='b_91fe993c2d93cde48679d6826_f7f097d693' tabindex='-1' value=''>

                        <input type='submit' value='Subscribe' placeholder='you@somewhere.com' name='subscribe' id='mc-embedded-subscribe'>
                    </form>

    ",
        css,
        &html
    )
}
<commit_msg>Fix tap target sizes for audit scores<commit_after>use copy_dir::copy_dir;
use pulldown_cmark::{html, Options, Parser};
use std::fs;

mod paths;
mod printing;

fn main() {
    printing::heading("Building website.");
    clean();
    generate();
    configure();
}

fn clean() {
    printing::subheading("Cleaning build directory.");
    paths::remove_dir(&paths::build());
}

fn generate() {
    printing::subheading("Generating build directory.");
    paths::create_dir(&paths::build());
    generate_public();
    generate_home();
    generate_code();
    generate_articles();
    generate_videos();
    generate_resume();
}

fn generate_public() {
    match copy_dir(&paths::public(), &paths::build().join("public")) {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

fn generate_home() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("index.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_file(
        &paths::build().join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_code() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("code.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("code"));
    paths::create_file(
        &paths::build().join("code").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_articles() {
    paths::create_dir(&paths::build().join("articles"));
    match fs::read_dir(&paths::content().join("articles")) {
        Ok(markdown_files) => {
            let mut markdown_links_to_routes = Vec::new();

            for markdown_file in markdown_files {
                let markdown_file = match &markdown_file {
                    Ok(markdown_file) => markdown_file.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let markdown_file_contents = match fs::read_to_string(&markdown_file) {
                    Ok(contents) => contents,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let route = &paths::file_stem(markdown_file);
                paths::create_dir(&paths::build().join("articles").join(route));
                paths::create_file(
                    &paths::build()
                        .join("articles")
                        .join(route)
                        .join("index.html"),
                    &markdown_to_html(&markdown_file_contents),
                );

                let title = match markdown_file_contents.lines().next() {
                    Some(title) => &title[2..],
                    None => panic!("Cannot find a title in {}.", &route),
                };
                markdown_links_to_routes
                    .push(format!("- [{}.](/articles/{})", title, route).to_string());
            }

            let markdown_articles_index = format!(
                "# Articles\nMy posts on software development.\n{}",
                markdown_links_to_routes.join("\n")
            );

            paths::create_file(
                &paths::build().join("articles").join("index.html"),
                &markdown_to_html(&markdown_articles_index),
            );
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };
}

fn generate_videos() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("videos.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("videos"));
    paths::create_file(
        &paths::build().join("videos").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn generate_resume() {
    let markdown_file_contents = match fs::read_to_string(&paths::content().join("resume.md")) {
        Ok(contents) => contents,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    paths::create_dir(&paths::build().join("resume"));
    paths::create_file(
        &paths::build().join("resume").join("index.html"),
        &markdown_to_html(&markdown_file_contents),
    );
}

fn configure() {
    printing::subheading("Configuring build directory.");

    // Adds a CNAME file for the host (GitHub Pages) and registrar (Hover) to use my custom domain name (trevordmiller.com)
    paths::create_file(&paths::build().join("CNAME"), "trevordmiller.com");
}

fn markdown_to_html(markdown: &str) -> std::string::String {
    let css = r#"
        html {
            font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
            color: #333333;
        }
        body {
            margin: 0;
        }
        header {
            background: #f5f5f5;
            border-bottom: 1px solid #d3d3d3;
        }
        header nav {
            max-width: 80ch;
            margin: 0 auto;
        }
        header a {
            padding: 1rem;
            font-family: "Courier New", Courier, monospace;
            text-decoration: none;
            color: #333333;
            display: block;
        }
        header a:first-of-type {
            display: flex;
            align-items: center;
        }
        header a:first-of-type::before {
            content: ">";
            margin: 0 1ch 0 0;
        }
        header a:first-of-type::after {
            content: "";
            margin: 0 0 0 1ch;
            display: inline-block;
            background: #333333;
            width: 1ch;
            height: 1em;
            animation: cursor 800ms infinite;
        }
        @keyframes cursor {
            0% {
                opacity: 0;
            }
            50% {
                opacity: 1;
            }
            to {
                opacity: 0;
            }
        }
        @media screen and (min-width: 80ch) {
            header {
                position: sticky;
                top: 0;
            }
            header nav {
                display: flex;
                align-items: center;
            }
            header a:first-of-type {
                flex: 1;
            }
        }
        main {
            max-width: 80ch;
            margin: 0 auto;
            padding: 1rem;
        }
        li {
            margin-bottom: 1rem;
        }
        pre code {
            overflow: auto;
            display: block;
            padding: 1rem;
            background: #f5f5f5;
            border-radius: 3px;
        }
        footer {
            max-width: 80ch;
            margin: 2rem auto 0 auto;
            padding: 2rem 1rem 1rem 1rem;
            border-top: 1px solid #d3d3d3;
        }
        label, input {
            display: block;
            font-size: 1rem;
            margin-bottom: 1rem;
        }
        input[type=submit] {
            -moz-appearance: none;
            -webkit-appearance: none;
            padding: 1rem;
            background: #333333;
            color: #ffffff;
            font-size: 1rem;
            border: 0 none;
            cursor: pointer;
            border-radius: 3px;
        }
    "#;

    let parser = Parser::new_ext(markdown, Options::empty());
    let mut html = String::new();
    html::push_html(&mut html, parser);

    format!("
        <!DOCTYPE html>

                <title>trevordmiller.com</title>
                <meta name='author' content='Trevor D. Miller'>
                <meta name='description' content='Personal website.'>
                <meta charset='utf-8'>
                <meta name='viewport' content='width=device-width, initial-scale=1'>

                        trevordmiller
                        Articles
                        Videos
                        Code
                        Resume

                    {}

Get my latest content by email

I won't sell or share your email. I won't send you spam. You can unsubscribe at any time.

                    <form action='https://trevordmiller.us10.list-manage.com/subscribe/post?u=91fe993c2d93cde48679d6826&id=f7f097d693' method='post' id='mc-embedded-subscribe-form' name='mc-embedded-subscribe-form'>
                        Email address:
                        <input type='email' required value='' name='EMAIL' id='mce-EMAIL'>

<input type='text' name='b_91fe993c2d93cde48679d6826_f7f097d693' tabindex='-1' value=''>

                        <input type='submit' value='Subscribe' placeholder='you@somewhere.com' name='subscribe' id='mc-embedded-subscribe'>
                    </form>

    ",
        css,
        &html
    )
}
<|endoftext|>


<commit_before>use calx_alg::{Deciban, clamp};
use calx_ecs::Entity;
use calx_grid::{Dir6, Prefab};
use command::CommandResult;
use components::BrainState;
use effect::{Damage, Effect};
use event::Event;
use form::Form;
use item::{MagicEffect, ItemType, Slot};
use location::Location;
use query::Query;
use rand::Rng;
use terraform::Terraform;
use terrain::Terrain;
use volume::Volume;
use world::{Ecs, Loadout};

/// World-mutating methods that are not exposed outside the crate.
pub trait Mutate: Query + Terraform + Sized {
    /// Advance world state after player input has been received.
    ///
    /// Returns CommandResult Ok(()) so can used to end result-returning methods.
    fn next_tick(&mut self) -> CommandResult;

    fn set_entity_location(&mut self, e: Entity, loc: Location);

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot);

    fn set_player(&mut self, player: Option<Entity>);

    /// Mark an entity as dead, but don't remove it from the system yet.
    fn kill_entity(&mut self, e: Entity);

    /// Remove an entity from the system.
    ///
    /// You generally do not want to call this directly. Mark the entity as dead and it will be
    /// removed at the end of the turn.
    fn remove_entity(&mut self, e: Entity);

    /// Compute field-of-view into entity's map memory.
    ///
    /// Does nothing for entities without a map memory component.
    fn do_fov(&mut self, e: Entity);

    /// Push an event to the event queue for this tick.
    fn push_event(&mut self, event: Event);

    /// Access the persistent random number generator.
    fn rng(&mut self) -> &mut ::Rng;

    /// Mutable access to ecs
    fn ecs_mut(&mut self) -> &mut Ecs;

    /// Standard deciban roll, convert to i32 and clamp out extremes.
    fn roll(&mut self) -> f32 { clamp(-20.0, 20.0, self.rng().gen::<Deciban>().0.round()) }

    /// Run AI for all autonomous mobs.
    fn ai_main(&mut self) {
        for npc in self.active_mobs().into_iter() {
            if !self.is_npc(npc) {
                continue;
            }
            if self.ticks_this_frame(npc) {
                self.run_ai_for(npc)
            }
        }
    }

    /// Run AI for one non-player-controlled creature.
    fn run_ai_for(&mut self, npc: Entity) {
        const WAKEUP_DISTANCE: i32 = 5;

        use components::BrainState::*;
        let brain_state = self.brain_state(npc).expect("Running AI for non-mob");
        match brain_state {
            Asleep => {
                // XXX: Only treat player mob as potential hostile.
                if let (Some(loc), Some(player), Some(player_loc)) =
                    (
                        self.location(npc),
                        self.player(),
                        self.player().map(|p| self.location(p)).unwrap_or(None),
                    )
                {
                    if self.player_sees(loc) {
                        // Okay, tricky spot. Player might be seeing mob across a portal, in
                        // which case we can't do naive distance check.
                        // This could have a helper method that finds chart distance to self in
                        // player's map memory.
                        //
                        // For now, let's just go with mobs past portals not waking up to
                        // player.

                        if loc.metric_distance(player_loc) <= WAKEUP_DISTANCE {
                            self.designate_enemy(npc, player);
                        }
                    }
                }
            }
            Hunting(target) => {
                if let (Some(my_loc), Some(target_loc)) =
                    (self.location(npc), self.location(target))
                {
                    // TODO: Pathfind around obstacles
                    if let Some(move_dir) = my_loc.dir6_towards(target_loc) {
                        let dest = my_loc.jump(self, move_dir);
                        if dest == target_loc {
                            let _ = self.entity_melee(npc, move_dir);
                        } else {
                            let _ = self.entity_step(npc, move_dir);
                        }
                    }
                }
            }
            PlayerControl => {}
        }
    }

    fn notify_attacked_by(&mut self, victim: Entity, attacker: Entity) {
        // TODO: Check if victim is already in close combat and don't disengage against new target
        // if it is.
        self.designate_enemy(victim, attacker);
    }

    fn designate_enemy(&mut self, e: Entity, target: Entity) {
        // TODO: Probably want this logic to be more complex eventually.
        if self.is_npc(e) {
            if self.brain_state(e) == Some(BrainState::Asleep) {
                self.shout(e);
            }
            if let Some(brain) = self.ecs_mut().brain.get_mut(e) {
                brain.state = BrainState::Hunting(target);
            }
        }
    }

    /// Make a mob shout according to its type.
    fn shout(&mut self, e: Entity) {
        // TODO: Create noise, wake up other nearby monsters.
        use components::ShoutType;
        if let Some(shout) = self.ecs().brain.get(e).map(|b| b.shout) {
            match shout {
                ShoutType::Shout => {
                    msg!(self, "The {} shouts angrily.", self.entity_name(e));
                }
                ShoutType::Hiss => {
                    msg!(self, "The {} hisses angrily.", self.entity_name(e));
                }
                ShoutType::Buzz => {
                    msg!(self, "The {} buzzes loudly.", self.entity_name(e));
                }
                ShoutType::Roar => {
                    msg!(self, "The {} roars angrily.", self.entity_name(e));
                }
                ShoutType::Gurgle => {
                    msg!(self, "The {} gurgles loudly.", self.entity_name(e));
                }
                ShoutType::Silent => {}
            }
        }
    }

    /// Remove destroyed entities from system
    fn clean_dead(&mut self) {
        let kill_list: Vec<Entity> = self.entities()
            .filter(|&&e| !self.is_alive(e))
            .cloned()
            .collect();

        for e in kill_list {
            self.remove_entity(e);
        }
    }

    fn place_entity(&mut self, e: Entity, mut loc: Location) {
        if self.is_item(e) {
            loc = self.empty_item_drop_location(loc);
        }
        self.set_entity_location(e, loc);
        self.after_entity_moved(e);
    }

    fn after_entity_moved(&mut self, e: Entity) { self.do_fov(e); }

    fn entity_step(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        let loc = self.location(e).ok_or(())?.jump(self, dir);
        if self.can_enter(e, loc) {
            self.place_entity(e, loc);
            return Ok(());
        }

        Err(())
    }

    fn entity_melee(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        if let Some(loc) = self.location(e) {
            if let Some(target) = self.mob_at(loc.jump(self, dir)) {

                // XXX: Using power stat for damage, should this be different?
                // Do +5 since dmg 1 is really, really useless.
                let damage = ::attack_damage(
                    self.roll(),
                    self.stats(e).attack,
                    5 + self.stats(e).power,
                    self.stats(target).defense + 2 * self.stats(target).armor,
                );

                if damage == 0 {
                    msg!(
                        self,
                        "{} misses {}.",
                        self.entity_name(e),
                        self.entity_name(target)
                    );
                } else {
                    msg!(
                        self,
                        "{} hits {} for {}.",
                        self.entity_name(e),
                        self.entity_name(target),
                        damage
                    );
                }
                self.damage(target, damage, Damage::Physical, Some(e));
                return Ok(());
            }
        }
        Err(())
    }

    fn damage(&mut self, e: Entity, amount: i32, damage_type: Damage, source: Option<Entity>) {
        if let Some(attacker) = source {
            self.notify_attacked_by(e, attacker);
        }

        let max_hp = self.max_hp(e);

        let mut kill = false;
        if let Some(health) = self.ecs_mut().health.get_mut(e) {
            if amount > 0 {
                health.wounds += amount;

                if health.wounds > max_hp {
                    kill = true;
                }
            }
        }

        if kill {
            if let Some(loc) = self.location(e) {
                if self.player_sees(loc) {
                    // TODO: message templating
                    msg!(
                        self,
                        "The {} {}.",
                        self.entity_name(e),
                        match damage_type {
                            Damage::Physical => "is killed",
                            Damage::Fire => "is burned to ash",
                            Damage::Electricity => "is electrocuted",
                            Damage::Cold => "shatters to frozen pieces",
                        }
                    );
                }
            }
            self.kill_entity(e);
        }
    }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity;

    fn deploy_prefab(&mut self, origin: Location, prefab: &Prefab<(Terrain, Vec<String>)>) {
        for (p, &(ref terrain, _)) in prefab.iter() {
            let loc = origin + p;

            // Annihilate any existing entities in the drop zone.
            let es = self.entities_at(loc);
            for &e in &es {
                self.remove_entity(e);
            }

            self.set_terrain(loc, *terrain);
        }

        // Spawn entities after all terrain is in place so that initial FOV is good.
        for (p, &(_, ref entities)) in prefab.iter() {
            let loc = origin + p;

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.spawn_player(loc);
                } else {
                    let form = Form::named(spawn).expect(&format!("Form '{}' not found!", spawn));
                    self.spawn(&form.loadout, loc);
                }
            }
        }
    }

    /// Special method for setting the player start position.
    ///
    /// If player already exists and is placed in the world, do nothing.
    ///
    /// If player exists, but is not placed in spatial, teleport the player here.
    ///
    /// If player does not exist, create the initial player entity.
    fn spawn_player(&mut self, loc: Location) {
        if let Some(player) = self.player() {
            if self.location(player).is_none() {
                // Teleport player from limbo.
                self.place_entity(player, loc);
            }
        } else {
            // Initialize new player object.
            let form = Form::named("player").expect("Player form not found");
            let player = self.spawn(&form.loadout, loc);
            self.set_player(Some(player));
        }
    }

    fn entity_take(&mut self, e: Entity, item: Entity) -> Result<(), ()> {
        // Only mobs can take items.
        if !self.is_mob(e) {
            return Err(());
        }

        if !self.is_item(item) {
            return Err(());
        }

        // Somehow trying to pick up something we're inside of. Pls don't break the universe.
        if self.entity_contains(item, e) {
            panic!("Trying to pick up an entity you are inside of. This shouldn't happen");
        }

        if let Some(slot) = self.free_bag_slot(e) {
            self.equip_item(item, e, slot);
            if self.is_player(e) {
                msg!(self, "Picked up {}", self.entity_name(item));
            }

            Ok(())
        } else {
            // No more inventory space
            Err(())
        }
    }

    /// Cast an undirected spell
    fn cast_spell(
        &mut self,
        _origin: Location,
        effect: Entity,
        _caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::UntargetedUsable(effect) =
            self.ecs().item.get(effect).ok_or(())?.item_type
        {
            match effect {
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    /// Cast a directed spell
    fn cast_directed_spell(
        &mut self,
        origin: Location,
        dir: Dir6,
        effect: Entity,
        caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::TargetedUsable(effect) = self.ecs().item.get(effect).ok_or(())?.item_type {
            match effect {
                MagicEffect::Fireball => {
                    const FIREBALL_RANGE: u32 = 9;
                    const FIREBALL_RADIUS: u32 = 2;
                    const FIREBALL_EFFECT: Effect = Effect::Hit {
                        amount: 6,
                        damage: Damage::Fire,
                    };
                    let center = self.projected_explosion_center(origin, dir, FIREBALL_RANGE);
                    let volume = self.sphere_volume(center, FIREBALL_RADIUS);
                    self.apply_effect(&FIREBALL_EFFECT, &volume, caster);
                }
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    fn apply_effect_to_entity(&mut self, effect: &Effect, target: Entity, source: Option<Entity>) {
        use effect::Effect::*;
        match effect {
            &Heal(_amount) => {
                unimplemented!();
            }
            &Hit { amount, damage } => {
                self.damage(target, amount as i32, damage, source);
            }
            &Confuse => {
                unimplemented!();
            }
            &MagicMap => {
                unimplemented!();
            }
        }
    }

    fn apply_effect_to(&mut self, effect: &Effect, loc: Location, source: Option<Entity>) {
        if let Some(mob) = self.mob_at(loc) {
            self.apply_effect_to_entity(effect, mob, source);
        }
    }

    fn apply_effect(&mut self, effect: &Effect, volume: &Volume, source: Option<Entity>) {
        for loc in &volume.0 {
            self.apply_effect_to(effect, *loc, source);
        }
    }
}
<commit_msg>Lightning spell<commit_after>use calx_alg::{Deciban, clamp};
use calx_ecs::Entity;
use calx_grid::{Dir6, Prefab};
use command::CommandResult;
use components::BrainState;
use effect::{Damage, Effect};
use event::Event;
use form::Form;
use item::{MagicEffect, ItemType, Slot};
use location::Location;
use query::Query;
use rand::{self, Rng};
use terraform::Terraform;
use terrain::Terrain;
use volume::Volume;
use world::{Ecs, Loadout};

/// World-mutating methods that are not exposed outside the crate.
pub trait Mutate: Query + Terraform + Sized {
    /// Advance world state after player input has been received.
    ///
    /// Returns CommandResult Ok(()) so can used to end result-returning methods.
    fn next_tick(&mut self) -> CommandResult;

    fn set_entity_location(&mut self, e: Entity, loc: Location);

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot);

    fn set_player(&mut self, player: Option<Entity>);

    /// Mark an entity as dead, but don't remove it from the system yet.
    fn kill_entity(&mut self, e: Entity);

    /// Remove an entity from the system.
    ///
    /// You generally do not want to call this directly. Mark the entity as dead and it will be
    /// removed at the end of the turn.
    fn remove_entity(&mut self, e: Entity);

    /// Compute field-of-view into entity's map memory.
    ///
    /// Does nothing for entities without a map memory component.
    fn do_fov(&mut self, e: Entity);

    /// Push an event to the event queue for this tick.
    fn push_event(&mut self, event: Event);

    /// Access the persistent random number generator.
    fn rng(&mut self) -> &mut ::Rng;

    /// Mutable access to ecs
    fn ecs_mut(&mut self) -> &mut Ecs;

    /// Standard deciban roll, convert to i32 and clamp out extremes.
    fn roll(&mut self) -> f32 { clamp(-20.0, 20.0, self.rng().gen::<Deciban>().0.round()) }

    /// Run AI for all autonomous mobs.
    fn ai_main(&mut self) {
        for npc in self.active_mobs().into_iter() {
            if !self.is_npc(npc) {
                continue;
            }
            if self.ticks_this_frame(npc) {
                self.run_ai_for(npc)
            }
        }
    }

    /// Run AI for one non-player-controlled creature.
    fn run_ai_for(&mut self, npc: Entity) {
        const WAKEUP_DISTANCE: i32 = 5;

        use components::BrainState::*;
        let brain_state = self.brain_state(npc).expect("Running AI for non-mob");
        match brain_state {
            Asleep => {
                // XXX: Only treat player mob as potential hostile.
                if let (Some(loc), Some(player), Some(player_loc)) =
                    (
                        self.location(npc),
                        self.player(),
                        self.player().map(|p| self.location(p)).unwrap_or(None),
                    )
                {
                    if self.player_sees(loc) {
                        // Okay, tricky spot. Player might be seeing mob across a portal, in
                        // which case we can't do naive distance check.
                        // This could have a helper method that finds chart distance to self in
                        // player's map memory.
                        //
                        // For now, let's just go with mobs past portals not waking up to
                        // player.

                        if loc.metric_distance(player_loc) <= WAKEUP_DISTANCE {
                            self.designate_enemy(npc, player);
                        }
                    }
                }
            }
            Hunting(target) => {
                if let (Some(my_loc), Some(target_loc)) =
                    (self.location(npc), self.location(target))
                {
                    // TODO: Pathfind around obstacles
                    if let Some(move_dir) = my_loc.dir6_towards(target_loc) {
                        let dest = my_loc.jump(self, move_dir);
                        if dest == target_loc {
                            let _ = self.entity_melee(npc, move_dir);
                        } else {
                            let _ = self.entity_step(npc, move_dir);
                        }
                    }
                }
            }
            PlayerControl => {}
        }
    }

    fn notify_attacked_by(&mut self, victim: Entity, attacker: Entity) {
        // TODO: Check if victim is already in close combat and don't disengage against new target
        // if it is.
        self.designate_enemy(victim, attacker);
    }

    fn designate_enemy(&mut self, e: Entity, target: Entity) {
        // TODO: Probably want this logic to be more complex eventually.
        if self.is_npc(e) {
            if self.brain_state(e) == Some(BrainState::Asleep) {
                self.shout(e);
            }
            if let Some(brain) = self.ecs_mut().brain.get_mut(e) {
                brain.state = BrainState::Hunting(target);
            }
        }
    }

    /// Make a mob shout according to its type.
    fn shout(&mut self, e: Entity) {
        // TODO: Create noise, wake up other nearby monsters.
        use components::ShoutType;
        if let Some(shout) = self.ecs().brain.get(e).map(|b| b.shout) {
            match shout {
                ShoutType::Shout => {
                    msg!(self, "The {} shouts angrily.", self.entity_name(e));
                }
                ShoutType::Hiss => {
                    msg!(self, "The {} hisses angrily.", self.entity_name(e));
                }
                ShoutType::Buzz => {
                    msg!(self, "The {} buzzes loudly.", self.entity_name(e));
                }
                ShoutType::Roar => {
                    msg!(self, "The {} roars angrily.", self.entity_name(e));
                }
                ShoutType::Gurgle => {
                    msg!(self, "The {} gurgles loudly.", self.entity_name(e));
                }
                ShoutType::Silent => {}
            }
        }
    }

    /// Remove destroyed entities from system
    fn clean_dead(&mut self) {
        let kill_list: Vec<Entity> = self.entities()
            .filter(|&&e| !self.is_alive(e))
            .cloned()
            .collect();

        for e in kill_list {
            self.remove_entity(e);
        }
    }

    fn place_entity(&mut self, e: Entity, mut loc: Location) {
        if self.is_item(e) {
            loc = self.empty_item_drop_location(loc);
        }
        self.set_entity_location(e, loc);
        self.after_entity_moved(e);
    }

    fn after_entity_moved(&mut self, e: Entity) { self.do_fov(e); }

    fn entity_step(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        let loc = self.location(e).ok_or(())?.jump(self, dir);
        if self.can_enter(e, loc) {
            self.place_entity(e, loc);
            return Ok(());
        }

        Err(())
    }

    fn entity_melee(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        if let Some(loc) = self.location(e) {
            if let Some(target) = self.mob_at(loc.jump(self, dir)) {

                // XXX: Using power stat for damage, should this be different?
                // Do +5 since dmg 1 is really, really useless.
                let damage = ::attack_damage(
                    self.roll(),
                    self.stats(e).attack,
                    5 + self.stats(e).power,
                    self.stats(target).defense + 2 * self.stats(target).armor,
                );

                if damage == 0 {
                    msg!(
                        self,
                        "{} misses {}.",
                        self.entity_name(e),
                        self.entity_name(target)
                    );
                } else {
                    msg!(
                        self,
                        "{} hits {} for {}.",
                        self.entity_name(e),
                        self.entity_name(target),
                        damage
                    );
                }
                self.damage(target, damage, Damage::Physical, Some(e));
                return Ok(());
            }
        }
        Err(())
    }

    fn damage(&mut self, e: Entity, amount: i32, damage_type: Damage, source: Option<Entity>) {
        if let Some(attacker) = source {
            self.notify_attacked_by(e, attacker);
        }

        let max_hp = self.max_hp(e);

        let mut kill = false;
        if let Some(health) = self.ecs_mut().health.get_mut(e) {
            if amount > 0 {
                health.wounds += amount;

                if health.wounds > max_hp {
                    kill = true;
                }
            }
        }

        if kill {
            if let Some(loc) = self.location(e) {
                if self.player_sees(loc) {
                    // TODO: message templating
                    msg!(
                        self,
                        "The {} {}.",
                        self.entity_name(e),
                        match damage_type {
                            Damage::Physical => "is killed",
                            Damage::Fire => "is burned to ash",
                            Damage::Electricity => "is electrocuted",
                            Damage::Cold => "shatters to frozen pieces",
                        }
                    );
                }
            }
            self.kill_entity(e);
        }
    }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity;

    fn deploy_prefab(&mut self, origin: Location, prefab: &Prefab<(Terrain, Vec<String>)>) {
        for (p, &(ref terrain, _)) in prefab.iter() {
            let loc = origin + p;

            // Annihilate any existing entities in the drop zone.
            let es = self.entities_at(loc);
            for &e in &es {
                self.remove_entity(e);
            }

            self.set_terrain(loc, *terrain);
        }

        // Spawn entities after all terrain is in place so that initial FOV is good.
        for (p, &(_, ref entities)) in prefab.iter() {
            let loc = origin + p;

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.spawn_player(loc);
                } else {
                    let form = Form::named(spawn).expect(&format!("Form '{}' not found!", spawn));
                    self.spawn(&form.loadout, loc);
                }
            }
        }
    }

    /// Special method for setting the player start position.
    ///
    /// If player already exists and is placed in the world, do nothing.
    ///
    /// If player exists, but is not placed in spatial, teleport the player here.
    ///
    /// If player does not exist, create the initial player entity.
    fn spawn_player(&mut self, loc: Location) {
        if let Some(player) = self.player() {
            if self.location(player).is_none() {
                // Teleport player from limbo.
                self.place_entity(player, loc);
            }
        } else {
            // Initialize new player object.
            let form = Form::named("player").expect("Player form not found");
            let player = self.spawn(&form.loadout, loc);
            self.set_player(Some(player));
        }
    }

    fn entity_take(&mut self, e: Entity, item: Entity) -> Result<(), ()> {
        // Only mobs can take items.
        if !self.is_mob(e) {
            return Err(());
        }

        if !self.is_item(item) {
            return Err(());
        }

        // Somehow trying to pick up something we're inside of. Pls don't break the universe.
        if self.entity_contains(item, e) {
            panic!("Trying to pick up an entity you are inside of. This shouldn't happen");
        }

        if let Some(slot) = self.free_bag_slot(e) {
            self.equip_item(item, e, slot);
            if self.is_player(e) {
                msg!(self, "Picked up {}", self.entity_name(item));
            }

            Ok(())
        } else {
            // No more inventory space
            Err(())
        }
    }

    /// Cast an undirected spell
    fn cast_spell(
        &mut self,
        origin: Location,
        effect: Entity,
        caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::UntargetedUsable(effect) =
            self.ecs().item.get(effect).ok_or(())?.item_type
        {
            match effect {
                MagicEffect::Lightning => {
                    const LIGHTNING_RANGE: u32 = 4;
                    const LIGHTNING_EFFECT: Effect = Effect::Hit {
                        amount: 12,
                        damage: Damage::Electricity,
                    };

                    // TODO: Make an API, more efficient lookup of entities within an area

                    let targets: Vec<Entity> = self.sphere_volume(origin, LIGHTNING_RANGE).0.into_iter()
                        .flat_map(|loc| self.entities_at(loc))
                        .filter(|&e| self.is_mob(e) && Some(e) != caster).collect();

                    let mut target = rand::sample(self.rng(), &targets, 1);

                    if let Some(target) = target.pop() {
                        msg!(self, "There is a peal of thunder.");
                        let loc = self.location(*target).unwrap();
                        self.apply_effect(&LIGHTNING_EFFECT, &Volume::point(loc), caster);
                    } else {
                        msg!(self, "The spell fizzles.");
                    }
                }
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    /// Cast a directed spell
    fn cast_directed_spell(
        &mut self,
        origin: Location,
        dir: Dir6,
        effect: Entity,
        caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::TargetedUsable(effect) = self.ecs().item.get(effect).ok_or(())?.item_type {
            match effect {
                MagicEffect::Fireball => {
                    const FIREBALL_RANGE: u32 = 9;
                    const FIREBALL_RADIUS: u32 = 2;
                    const FIREBALL_EFFECT: Effect = Effect::Hit {
                        amount: 6,
                        damage: Damage::Fire,
                    };
                    let center = self.projected_explosion_center(origin, dir, FIREBALL_RANGE);
                    let volume = self.sphere_volume(center, FIREBALL_RADIUS);
                    self.apply_effect(&FIREBALL_EFFECT, &volume, caster);
                }
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    fn apply_effect_to_entity(&mut self, effect: &Effect, target: Entity, source: Option<Entity>) {
        use effect::Effect::*;
        match effect {
            &Heal(_amount) => {
                unimplemented!();
            }
            &Hit { amount, damage } => {
                self.damage(target, amount as i32, damage, source);
            }
            &Confuse => {
                unimplemented!();
            }
            &MagicMap => {
                unimplemented!();
            }
        }
    }

    fn apply_effect_to(&mut self, effect: &Effect, loc: Location, source: Option<Entity>) {
        if let Some(mob) = self.mob_at(loc) {
            self.apply_effect_to_entity(effect, mob, source);
        }
    }

    fn apply_effect(&mut self, effect: &Effect, volume: &Volume, source: Option<Entity>) {
        for loc in &volume.0 {
            self.apply_effect_to(effect, *loc, source);
        }
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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.

use syntax::ext::base::ExtCtxt;
use syntax::parse::new_parse_sess_special_handler;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::diagnostic::{Emitter, RenderSpan, Level, mk_span_handler, mk_handler};
use syntax::codemap;
use syntax::codemap::{Span, CodeMap};
use std::gc::Gc;

use parser::Parser;
use node;

#[test]
fn parse_node_with_no_name() {
  with_parsed_node("root {}", |node| {
    assert!(node.name == None);
    assert!(node.path == "root".to_str());
    assert!(node.attributes.len() == 0);
    assert!(node.subnodes.len() == 0);
  });
}

#[test]
fn parse_node_with_name() {
  with_parsed_node("test@root {}", |node| {
    assert!(node.name == Some("test".to_str()));
    assert!(node.path == "root".to_str());
    assert!(node.attributes.len() == 0);
    assert!(node.subnodes.len() == 0);
  });
}

#[test]
fn fails_to_parse_node_with_bad_name() {
  fails_to_parse("1@root {}");
  fails_to_parse("@root {}");
  fails_to_parse("+@root {}");
}

#[test]
fn fails_to_parse_node_with_bad_path() {
  fails_to_parse("test root {}");
  fails_to_parse("test@ {}");
  fails_to_parse("test@- {}");
}

#[test]
fn parse_node_with_no_body() {
  with_parsed_node("test@root;", |_| {
    assert!(true);
  });
}

#[test]
fn parse_node_with_numeric_path() {
  with_parsed_node("test@1 {}", |node| {
    assert!(node.path == "1".to_str());
  });
}

#[test]
fn parse_string_attribute() {
  with_parsed_node("test@root { key = \"value\"; second = \"other\"; }", |node| {
    assert!(node.get_string_attr("key") == Some(&"value".to_str()));
  });
}

#[test]
fn fails_to_parse_malformed_attibute() {
  fails_to_parse("test@root { 1 = \"value\"; }");
  fails_to_parse("test@root { k = v; }");
  fails_to_parse("test@root { k = \"value\" }");
}

#[test]
fn fails_to_parse_duplicate_attributes() {
  fails_to_parse("test@root { a = \"1\"; a = \"2\"; }");
}

#[test]
fn parse_integer_attribute() {
  with_parsed_node("test@root { key = 10; second = \"other\"; }", |node| {
    assert!(node.get_int_attr("key") == Some(10));
  });
}

// helpers
fn fails_to_parse(src: &str) {
  with_parsed_tts(src, |failed, pt| {
    assert!(failed == true);
    assert!(pt.is_none());
  });
}

fn with_parsed(src: &str, block: |node: &node::PlatformTree|) {
  with_parsed_tts(src, |failed, pt| {
    assert!(failed == false);
    block(&pt.unwrap());
  });
}

fn with_parsed_node(src: &str, block: |node: &Gc<node::Node>|) {
  with_parsed(src, |pt| {
    assert!(pt.nodes.len() == 1);
    block(pt.nodes.get(0));
  });
}

fn with_parsed_tts(src: &str, block: |bool, Option<node::PlatformTree>|) {
  let mut failed = false;
  let failptr = &mut failed as *mut bool;
  let ce = box CustomEmmiter::new(failptr);
  let sh = mk_span_handler(mk_handler(ce), CodeMap::new());
  let parse_sess = new_parse_sess_special_handler(sh);
  let cfg = Vec::new();
  let ecfg = ExpansionConfig {
    deriving_hash_type_parameter: false,
    crate_id: from_str("test").unwrap(),
  };
  let cx = ExtCtxt::new(&parse_sess, cfg, ecfg);
  let tts = cx.parse_tts(src.to_str());

  let mut parser = Parser::new(&cx, tts.as_slice());
  let nodes = parser.parse_platformtree();

  block(failed, nodes);
}

struct CustomEmmiter {
  failed: *mut bool
}

impl CustomEmmiter {
  pub fn new(fp: *mut bool) -> CustomEmmiter {
    CustomEmmiter {
      failed: fp,
    }
  }
}

impl Emitter for CustomEmmiter {
  fn emit(&mut self, _: Option<(&codemap::CodeMap, Span)>, m: &str, l: Level) {
    unsafe { *self.failed = true };
    println!("{} {}", l, m);
  }
  fn custom_emit(&mut self, _: &codemap::CodeMap, _: RenderSpan, _: &str,
      _: Level) {
    fail!();
  }
}
<commit_msg>Bigger test coverage for bad attributes.<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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.

use syntax::ext::base::ExtCtxt;
use syntax::parse::new_parse_sess_special_handler;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::diagnostic::{Emitter, RenderSpan, Level, mk_span_handler, mk_handler};
use syntax::codemap;
use syntax::codemap::{Span, CodeMap};
use std::gc::Gc;

use parser::Parser;
use node;

#[test]
fn parse_node_with_no_name() {
  with_parsed_node("root {}", |node| {
    assert!(node.name == None);
    assert!(node.path == "root".to_str());
    assert!(node.attributes.len() == 0);
    assert!(node.subnodes.len() == 0);
  });
}

#[test]
fn parse_node_with_name() {
  with_parsed_node("test@root {}", |node| {
    assert!(node.name == Some("test".to_str()));
    assert!(node.path == "root".to_str());
    assert!(node.attributes.len() == 0);
    assert!(node.subnodes.len() == 0);
  });
}

#[test]
fn fails_to_parse_node_with_bad_name() {
  fails_to_parse("1@root {}");
  fails_to_parse("@root {}");
  fails_to_parse("+@root {}");
}

#[test]
fn fails_to_parse_node_with_bad_path() {
  fails_to_parse("test root {}");
  fails_to_parse("test@ {}");
  fails_to_parse("test@- {}");
}

#[test]
fn parse_node_with_no_body() {
  with_parsed_node("test@root;", |_| {
    assert!(true);
  });
}

#[test]
fn parse_node_with_numeric_path() {
  with_parsed_node("test@1 {}", |node| {
    assert!(node.path == "1".to_str());
  });
}

#[test]
fn parse_string_attribute() {
  with_parsed_node("test@root { key = \"value\"; second = \"other\"; }", |node| {
    assert!(node.get_string_attr("key") == Some(&"value".to_str()));
  });
}

#[test]
fn parse_integer_attribute() {
  with_parsed_node("test@root { key = 10; second = \"other\"; }", |node| {
    assert!(node.get_int_attr("key") == Some(10));
  });
}

#[test]
fn fails_to_parse_duplicate_attributes() {
  fails_to_parse("test@root { a = 1; a = \"2\"; }");
}

#[test]
fn fails_to_parse_malformed_attibute() {
  fails_to_parse("test@root { k = \"value\" }");
  fails_to_parse("test@root { 1 = \"value\"; }");
  fails_to_parse("test@root { k = v; }");
  fails_to_parse("test@root { k = 10u8; }");
  fails_to_parse("test@root { k = 10i8; }");
  fails_to_parse("test@root { k = -42; }");
}

// helpers
fn fails_to_parse(src: &str) {
  with_parsed_tts(src, |failed, pt| {
    assert!(failed == true);
    assert!(pt.is_none());
  });
}

fn with_parsed(src: &str, block: |node: &node::PlatformTree|) {
  with_parsed_tts(src, |failed, pt| {
    assert!(failed == false);
    block(&pt.unwrap());
  });
}

fn with_parsed_node(src: &str, block: |node: &Gc<node::Node>|) {
  with_parsed(src, |pt| {
    assert!(pt.nodes.len() == 1);
    block(pt.nodes.get(0));
  });
}

fn with_parsed_tts(src: &str, block: |bool, Option<node::PlatformTree>|) {
  let mut failed = false;
  let failptr = &mut failed as *mut bool;
  let ce = box CustomEmmiter::new(failptr);
  let sh = mk_span_handler(mk_handler(ce), CodeMap::new());
  let parse_sess = new_parse_sess_special_handler(sh);
  let cfg = Vec::new();
  let ecfg = ExpansionConfig {
    deriving_hash_type_parameter: false,
    crate_id: from_str("test").unwrap(),
  };
  let cx = ExtCtxt::new(&parse_sess, cfg, ecfg);
  let tts = cx.parse_tts(src.to_str());

  let mut parser = Parser::new(&cx, tts.as_slice());
  let nodes = parser.parse_platformtree();

  block(failed, nodes);
}

struct CustomEmmiter {
  failed: *mut bool
}

impl CustomEmmiter {
  pub fn new(fp: *mut bool) -> CustomEmmiter {
    CustomEmmiter {
      failed: fp,
    }
  }
}

impl Emitter for CustomEmmiter {
  fn emit(&mut self, _: Option<(&codemap::CodeMap, Span)>, m: &str, l: Level) {
    unsafe { *self.failed = true };
    println!("{} {}", l, m);
  }
  fn custom_emit(&mut self, _: &codemap::CodeMap, _: RenderSpan, _: &str,
      _: Level) {
    fail!();
  }
}
<|endoftext|>