<commit_before>// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//      ==== Address map in use in ARM development systems today ====
//
//              - 32-bit -              - 36-bit -          - 40-bit -
//1024GB    +                   +                      +-------------------+     <- 40-bit
//          |                                           | DRAM              |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//          |                                           |                   |
//          |                                           |                   |
//          |                                           |                   |
//544GB     +                   +                       +-------------------+
//          |                                           | Hole or DRAM      |
//          |                                           |                   |
//512GB     +                   +                       +-------------------+
//          |                                           |       Mapped      |
//          |                                           |       I/O         |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//256GB     +                   +                       +-------------------+
//          |                                           |       Reserved    |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//64GB      +                   +-----------------------+-------------------+   <- 36-bit
//          |                   |                   DRAM                    |
//          ~                   ~                   ~                       ~
//          |                   |                                           |
//          |                   |                                           |
//34GB      +                   +-----------------------+-------------------+
//          |                   |                  Hole or DRAM             |
//32GB      +                   +-----------------------+-------------------+
//          |                   |                   Mapped I/O              |
//          ~                   ~                       ~                   ~
//          |                   |                                           |
//16GB      +                   +-----------------------+-------------------+
//          |                   |                   Reserved                |
//          ~                   ~                       ~                   ~
//4GB       +-------------------+-----------------------+-------------------+   <- 32-bit
//          |           2GB of DRAM                                         |
//          |                                                               |
//2GB       +-------------------+-----------------------+-------------------+
//          |                           Mapped I/O                          |
//1GB       +-------------------+-----------------------+-------------------+
//          |                          ROM & RAM & I/O                      |
//0GB       +-------------------+-----------------------+-------------------+   0
//              - 32-bit -              - 36-bit -              - 40-bit -
//
// Taken from (http://infocenter.arm.com/help/topic/com.arm.doc.den0001c/DEN0001C_principles_of_arm_memory_maps.pdf).

/// Start of RAM on 64 bit ARM.
pub const DRAM_MEM_START: u64 = 0x8000_0000; // 2 GB.
/// The maximum addressable RAM address.
pub const DRAM_MEM_END: u64 = 0x00FF_8000_0000; // 1024 - 2 = 1022 GB.
/// The maximum RAM size.
pub const DRAM_MEM_MAX_SIZE: u64 = DRAM_MEM_END - DRAM_MEM_START;

/// Kernel command line maximum size.
/// As per `arch/arm64/include/uapi/asm/setup.h`.
pub const CMDLINE_MAX_SIZE: usize = 2048;

/// Maximum size of the device tree blob as specified in https://www.kernel.org/doc/Documentation/arm64/booting.txt.
pub const FDT_MAX_SIZE: usize = 0x20_0000;

// As per virt/kvm/arm/vgic/vgic-kvm-device.c we need
// the number of interrupts our GIC will support to be:
// * bigger than 32
// * less than 1023 and
// * a multiple of 32.
// We are setting up our interrupt controller to support a maximum of 128 interrupts.
/// First usable interrupt on aarch64.
pub const IRQ_BASE: u32 = 32;

/// Last usable interrupt on aarch64.
pub const IRQ_MAX: u32 = 159;

/// Below this address will reside the GIC, above this address will reside the MMIO devices.
pub const MAPPED_IO_START: u64 = 1 << 30; // 1 GB
<commit_msg>aarch64: fix DRAM_MEM_END in layout.rs<commit_after>// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//      ==== Address map in use in ARM development systems today ====
//
//              - 32-bit -              - 36-bit -          - 40-bit -
//1024GB    +                   +                      +-------------------+     <- 40-bit
//          |                                           | DRAM              |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//          |                                           |                   |
//          |                                           |                   |
//          |                                           |                   |
//544GB     +                   +                       +-------------------+
//          |                                           | Hole or DRAM      |
//          |                                           |                   |
//512GB     +                   +                       +-------------------+
//          |                                           |       Mapped      |
//          |                                           |       I/O         |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//256GB     +                   +                       +-------------------+
//          |                                           |       Reserved    |
//          ~                   ~                       ~                   ~
//          |                                           |                   |
//64GB      +                   +-----------------------+-------------------+   <- 36-bit
//          |                   |                   DRAM                    |
//          ~                   ~                   ~                       ~
//          |                   |                                           |
//          |                   |                                           |
//34GB      +                   +-----------------------+-------------------+
//          |                   |                  Hole or DRAM             |
//32GB      +                   +-----------------------+-------------------+
//          |                   |                   Mapped I/O              |
//          ~                   ~                       ~                   ~
//          |                   |                                           |
//16GB      +                   +-----------------------+-------------------+
//          |                   |                   Reserved                |
//          ~                   ~                       ~                   ~
//4GB       +-------------------+-----------------------+-------------------+   <- 32-bit
//          |           2GB of DRAM                                         |
//          |                                                               |
//2GB       +-------------------+-----------------------+-------------------+
//          |                           Mapped I/O                          |
//1GB       +-------------------+-----------------------+-------------------+
//          |                          ROM & RAM & I/O                      |
//0GB       +-------------------+-----------------------+-------------------+   0
//              - 32-bit -              - 36-bit -              - 40-bit -
//
// Taken from (http://infocenter.arm.com/help/topic/com.arm.doc.den0001c/DEN0001C_principles_of_arm_memory_maps.pdf).

/// Start of RAM on 64 bit ARM.
pub const DRAM_MEM_START: u64 = 0x8000_0000; // 2 GB.
/// The maximum RAM size.
pub const DRAM_MEM_MAX_SIZE: u64 = 0x00FF_8000_0000; // 1024 - 2 = 1022G.

/// Kernel command line maximum size.
/// As per `arch/arm64/include/uapi/asm/setup.h`.
pub const CMDLINE_MAX_SIZE: usize = 2048;

/// Maximum size of the device tree blob as specified in https://www.kernel.org/doc/Documentation/arm64/booting.txt.
pub const FDT_MAX_SIZE: usize = 0x20_0000;

// As per virt/kvm/arm/vgic/vgic-kvm-device.c we need
// the number of interrupts our GIC will support to be:
// * bigger than 32
// * less than 1023 and
// * a multiple of 32.
// We are setting up our interrupt controller to support a maximum of 128 interrupts.
/// First usable interrupt on aarch64.
pub const IRQ_BASE: u32 = 32;

/// Last usable interrupt on aarch64.
pub const IRQ_MAX: u32 = 159;

/// Below this address will reside the GIC, above this address will reside the MMIO devices.
pub const MAPPED_IO_START: u64 = 1 << 30; // 1 GB
<|endoftext|>


<commit_before>extern crate protobuf;
extern crate rand;
extern crate time;

use std::fs::File;
use std::path::Path;

use rand::Rng;
use rand::StdRng;
use rand::SeedableRng;

use protobuf::Message;
use perftest_data::PerftestData;

mod perftest_data;

fn measure_ns<R, F : FnMut() -> R>(mut f: F) -> (u64, R) {
    let start = time::precise_time_ns();
    let r = f();
    (time::precise_time_ns() - start, r)
}

fn measure_and_print<R, F : FnMut() -> R>(title: &str, iter: u64, f: F) -> R {
    let (ns, r) = measure_ns(f);
    let ns_per_iter = ns / iter;
    println!("{}: {} ns per iter", title, ns_per_iter);
    r
}

struct TestRunner {
    data_size: u32,
    selected: Option<String>,
    any_matched: bool,
}

impl TestRunner {
    fn run_test<M : Message + Clone + PartialEq>(&self, name: &str, data: &[M]) {
        assert!(data.len() > 0, "empty string for test: {}", name);

        let mut rng: StdRng = SeedableRng::from_seed([
            10, 20, 30, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        ]);

        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.compute_size();
        }

        let mut buf = Vec::new();
        measure_and_print(&format!("{}: write", name), random_data.len() as u64, || {
            for m in &random_data {
                m.write_length_delimited_to_vec(&mut buf).unwrap();
            }
        });

        let read_data =
            measure_and_print(&format!("{}: read", name), random_data.len() as u64, || {
                let mut r = Vec::new();
                let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
                while !coded_input_stream.eof().unwrap() {
                    r.push(
                        protobuf::parse_length_delimited_from::<M>(&mut coded_input_stream)
                            .unwrap(),
                    );
                }
                r
            });

        assert_eq!(random_data, read_data);

        let merged = measure_and_print(
            &format!("{}: read reuse", name),
            random_data.len() as u64,
            || {
                let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
                let mut msg: M = Message::new();
                let mut count = 0;
                while !coded_input_stream.eof().unwrap() {
                    msg.clear();
                    coded_input_stream.merge_message(&mut msg).unwrap();
                    count += 1;
                }
                count
            },
        );

        assert_eq!(random_data.len(), merged);
    }

    fn test<M : Message + Clone + PartialEq>(&mut self, name: &str, data: &[M]) {
        if self.selected.as_ref().map(|s| *s == name).unwrap_or(true) {
            self.run_test(name, data);
            self.any_matched = true;
        }
    }

    fn check(&self) {
        if !self.any_matched {
            let name = self.selected.as_ref().map(|s| &s[..]).unwrap_or("bug");
            panic!("no tests found with name {}", name);
        }
    }
}

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    if args.len() > 3 {
        panic!("usage: {} [data_size] [test]", args[0])
    }
    let data_size = args.iter()
        .nth(1)
        .map(|x| x.parse().unwrap())
        .unwrap_or(1000000);
    let selected = args.iter().nth(2).cloned();

    let mut runner = TestRunner {
        selected: selected,
        any_matched: false,
        data_size: data_size,
    };

    let mut is = File::open(&Path::new("perftest_data.pbbin")).unwrap();
    let test_data = protobuf::parse_from_reader::<PerftestData>(&mut is).unwrap();

    runner.test("test1", test_data.get_test1());
    runner.test("test_repeated_bool", test_data.get_test_repeated_bool());
    runner.test(
        "test_repeated_packed_int32",
        test_data.get_test_repeated_packed_int32(),
    );
    runner.test(
        "test_repeated_messages",
        test_data.get_test_repeated_messages(),
    );
    runner.test(
        "test_optional_messages",
        test_data.get_test_optional_messages(),
    );
    runner.test("test_strings", test_data.get_test_strings());
    runner.test(
        "test_small_bytearrays",
        test_data.get_test_small_bytearrays(),
    );
    runner.test(
        "test_large_bytearrays",
        test_data.get_test_large_bytearrays(),
    );
    runner.check();
}
<commit_msg>Fix compilation<commit_after>extern crate protobuf;
extern crate rand;
extern crate time;

use std::fs::File;
use std::path::Path;

use rand::Rng;
use rand::StdRng;
use rand::SeedableRng;

use protobuf::Message;
use perftest_data::PerftestData;

mod perftest_data;

fn measure_ns<R, F : FnMut() -> R>(mut f: F) -> (u64, R) {
    let start = time::precise_time_ns();
    let r = f();
    (time::precise_time_ns() - start, r)
}

fn measure_and_print<R, F : FnMut() -> R>(title: &str, iter: u64, f: F) -> R {
    let (ns, r) = measure_ns(f);
    let ns_per_iter = ns / iter;
    println!("{}: {} ns per iter", title, ns_per_iter);
    r
}

struct TestRunner {
    data_size: u32,
    selected: Option<String>,
    any_matched: bool,
}

impl TestRunner {
    fn run_test<M : Message + Clone + PartialEq>(&self, name: &str, data: &[M]) {
        assert!(data.len() > 0, "empty string for test: {}", name);

        let mut rng: StdRng = SeedableRng::from_seed([
            10, 20, 30, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        ]);

        let mut random_data: Vec<M> = Vec::new();

        let mut total_size = 0;
        while total_size < self.data_size {
            let ref item = data[rng.gen_range(0, data.len())];
            random_data.push(item.clone());
            total_size += item.compute_size();
        }

        let mut buf = Vec::new();
        measure_and_print(&format!("{}: write", name), random_data.len() as u64, || {
            for m in &random_data {
                m.write_length_delimited_to_vec(&mut buf).unwrap();
            }
        });

        let read_data =
            measure_and_print(&format!("{}: read", name), random_data.len() as u64, || {
                let mut r = Vec::new();
                let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
                while !coded_input_stream.eof().unwrap() {
                    r.push(coded_input_stream.read_message().unwrap());
                }
                r
            });

        assert_eq!(random_data, read_data);

        let merged = measure_and_print(
            &format!("{}: read reuse", name),
            random_data.len() as u64,
            || {
                let mut coded_input_stream = protobuf::CodedInputStream::from_bytes(&buf);
                let mut msg: M = Message::new();
                let mut count = 0;
                while !coded_input_stream.eof().unwrap() {
                    msg.clear();
                    coded_input_stream.merge_message(&mut msg).unwrap();
                    count += 1;
                }
                count
            },
        );

        assert_eq!(random_data.len(), merged);
    }

    fn test<M : Message + Clone + PartialEq>(&mut self, name: &str, data: &[M]) {
        if self.selected.as_ref().map(|s| *s == name).unwrap_or(true) {
            self.run_test(name, data);
            self.any_matched = true;
        }
    }

    fn check(&self) {
        if !self.any_matched {
            let name = self.selected.as_ref().map(|s| &s[..]).unwrap_or("bug");
            panic!("no tests found with name {}", name);
        }
    }
}

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    if args.len() > 3 {
        panic!("usage: {} [data_size] [test]", args[0])
    }
    let data_size = args.iter()
        .nth(1)
        .map(|x| x.parse().unwrap())
        .unwrap_or(1000000);
    let selected = args.iter().nth(2).cloned();

    let mut runner = TestRunner {
        selected: selected,
        any_matched: false,
        data_size: data_size,
    };

    let mut is = File::open(&Path::new("perftest_data.pbbin")).unwrap();
    let test_data = protobuf::parse_from_reader::<PerftestData>(&mut is).unwrap();

    runner.test("test1", test_data.get_test1());
    runner.test("test_repeated_bool", test_data.get_test_repeated_bool());
    runner.test(
        "test_repeated_packed_int32",
        test_data.get_test_repeated_packed_int32(),
    );
    runner.test(
        "test_repeated_messages",
        test_data.get_test_repeated_messages(),
    );
    runner.test(
        "test_optional_messages",
        test_data.get_test_optional_messages(),
    );
    runner.test("test_strings", test_data.get_test_strings());
    runner.test(
        "test_small_bytearrays",
        test_data.get_test_small_bytearrays(),
    );
    runner.test(
        "test_large_bytearrays",
        test_data.get_test_large_bytearrays(),
    );
    runner.check();
}
<|endoftext|>


<commit_before>use crate::core::{
    prelude::*,
    util::{parse::parse_url_param, validate::Validate},
};

#[rustfmt::skip]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UpdateEntry {
    pub osm_node       : Option<u64>,
    pub version        : u64,
    pub title          : String,
    pub description    : String,
    pub lat            : f64,
    pub lng            : f64,
    pub street         : Option<String>,
    pub zip            : Option<String>,
    pub city           : Option<String>,
    pub country        : Option<String>,
    pub email          : Option<String>,
    pub telephone      : Option<String>,
    pub homepage       : Option<String>,
    pub categories     : Vec<String>,
    pub tags           : Vec<String>,
    pub image_url      : Option<String>,
    pub image_link_url : Option<String>,
}

pub struct Storable(Entry);

impl Storable {
    pub fn entry_id(&self) -> &str {
        &self.0.id
    }
}

pub fn prepare_updated_entry<D: Db>(db: &D, id: String, e: UpdateEntry) -> Result<Storable> {
    let old: Entry = db.get_entry(&id)?;
    if (old.version + 1) != e.version {
        return Err(Error::Repo(RepoError::InvalidVersion));
    }
    let UpdateEntry {
        version,
        title,
        description,
        lat,
        lng,
        street,
        zip,
        city,
        country,
        email,
        telephone,
        categories,
        tags,
        ..
    } = e;
    let pos = match MapPoint::try_from_lat_lng_deg(lat, lng) {
        None => return Err(ParameterError::InvalidPosition.into()),
        Some(pos) => pos,
    };
    let tags = super::prepare_tag_list(tags);
    super::check_and_count_owned_tags(db, &tags, None)?;
    super::check_and_count_owned_tags(db, &tags, org.as_ref())?;
    // TODO: Ensure that no reserved tags are removed without authorization.
    // All existing reserved tags from other organizations must be preserved
    // when editing entries. Reserved tags that already exist should not be
    // considers during the check, because they must be preserved independent
    // of who is editing the entry.
    // GitHub issue: https://github.com/slowtec/openfairdb/issues/203
    let address = Address {
        street,
        zip,
        city,
        country,
    };
    let address = if address.is_empty() {
        None
    } else {
        Some(address)
    };
    let e = Entry {
        id,
        osm_node: None,
        created: Timestamp::now(),
        archived: None,
        version,
        title,
        description,
        location: Location { pos, address },
        contact: Some(Contact { email, telephone }),
        homepage: e.homepage.map(|ref url| parse_url_param(url)).transpose()?,
        categories,
        tags,
        license: old.license, // license is immutable
        image_url: e
            .image_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
        image_link_url: e
            .image_link_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
    };
    e.validate()?;
    Ok(Storable(e))
}

pub fn store_updated_entry<D: Db>(db: &D, s: Storable) -> Result<(Entry, Vec<Rating>)> {
    let Storable(entry) = s;
    debug!("Storing updated entry: {:?}", entry);
    for t in &entry.tags {
        db.create_tag_if_it_does_not_exist(&Tag { id: t.clone() })?;
    }
    db.update_entry(&entry)?;
    let ratings = db.load_ratings_of_entry(&entry.id)?;
    Ok((entry, ratings))
}

#[cfg(test)]
mod tests {

    use super::super::tests::MockDb;
    use super::*;
    use uuid::Uuid;

    #[test]
    fn update_valid_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .title("foo")
            .description("bar")
            .image_url(Some("http://img"))
            .image_link_url(Some("http://imglink"))
            .license(Some("CC0-1.0"))
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : Some("img2".into()),
            image_link_url: old.image_link_url.clone(),
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let now = Timestamp::now();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        assert_eq!(mock_db.entries.borrow().len(), 1);
        let x = &mock_db.entries.borrow()[0];
        assert_eq!(
            "street",
            x.location
                .address
                .as_ref()
                .unwrap()
                .street
                .as_ref()
                .unwrap()
        );
        assert_eq!("bar", x.description);
        assert_eq!(2, x.version);
        assert!(x.created >= now);
        assert_eq!(None, x.archived);
        assert!(Uuid::parse_str(&x.id).is_ok());
        assert_eq!("https://www.img2/", x.image_url.as_ref().unwrap());
        assert_eq!("http://imglink/", x.image_link_url.as_ref().unwrap());
    }

    #[test]
    fn update_entry_with_invalid_version() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(3)
            .title("foo")
            .description("bar")
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 3,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::InvalidVersion => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 1);
    }

    #[test]
    fn update_non_existing_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 4,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::NotFound => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 0);
    }

    #[test]
    fn update_valid_entry_with_tags() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .tags(vec!["bio", "fair"])
            .license(Some("CC0-1.0"))
            .finish();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec!["vegan".into()],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        mock_db.tags = vec![Tag { id: "bio".into() }, Tag { id: "fair".into() }].into();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        let e = mock_db.get_entry(&id).unwrap();
        assert_eq!(None, e.archived);
        assert_eq!(e.tags, vec!["vegan"]);
        assert_eq!(mock_db.tags.borrow().len(), 3);
    }
}
<commit_msg>Delete line of code that has accidentally been inserted<commit_after>use crate::core::{
    prelude::*,
    util::{parse::parse_url_param, validate::Validate},
};

#[rustfmt::skip]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UpdateEntry {
    pub osm_node       : Option<u64>,
    pub version        : u64,
    pub title          : String,
    pub description    : String,
    pub lat            : f64,
    pub lng            : f64,
    pub street         : Option<String>,
    pub zip            : Option<String>,
    pub city           : Option<String>,
    pub country        : Option<String>,
    pub email          : Option<String>,
    pub telephone      : Option<String>,
    pub homepage       : Option<String>,
    pub categories     : Vec<String>,
    pub tags           : Vec<String>,
    pub image_url      : Option<String>,
    pub image_link_url : Option<String>,
}

pub struct Storable(Entry);

impl Storable {
    pub fn entry_id(&self) -> &str {
        &self.0.id
    }
}

pub fn prepare_updated_entry<D: Db>(db: &D, id: String, e: UpdateEntry) -> Result<Storable> {
    let old: Entry = db.get_entry(&id)?;
    if (old.version + 1) != e.version {
        return Err(Error::Repo(RepoError::InvalidVersion));
    }
    let UpdateEntry {
        version,
        title,
        description,
        lat,
        lng,
        street,
        zip,
        city,
        country,
        email,
        telephone,
        categories,
        tags,
        ..
    } = e;
    let pos = match MapPoint::try_from_lat_lng_deg(lat, lng) {
        None => return Err(ParameterError::InvalidPosition.into()),
        Some(pos) => pos,
    };
    let tags = super::prepare_tag_list(tags);
    super::check_and_count_owned_tags(db, &tags, None)?;
    // TODO: Ensure that no reserved tags are removed without authorization.
    // All existing reserved tags from other organizations must be preserved
    // when editing entries. Reserved tags that already exist should not be
    // considers during the check, because they must be preserved independent
    // of who is editing the entry.
    // GitHub issue: https://github.com/slowtec/openfairdb/issues/203
    let address = Address {
        street,
        zip,
        city,
        country,
    };
    let address = if address.is_empty() {
        None
    } else {
        Some(address)
    };
    let e = Entry {
        id,
        osm_node: None,
        created: Timestamp::now(),
        archived: None,
        version,
        title,
        description,
        location: Location { pos, address },
        contact: Some(Contact { email, telephone }),
        homepage: e.homepage.map(|ref url| parse_url_param(url)).transpose()?,
        categories,
        tags,
        license: old.license, // license is immutable
        image_url: e
            .image_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
        image_link_url: e
            .image_link_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
    };
    e.validate()?;
    Ok(Storable(e))
}

pub fn store_updated_entry<D: Db>(db: &D, s: Storable) -> Result<(Entry, Vec<Rating>)> {
    let Storable(entry) = s;
    debug!("Storing updated entry: {:?}", entry);
    for t in &entry.tags {
        db.create_tag_if_it_does_not_exist(&Tag { id: t.clone() })?;
    }
    db.update_entry(&entry)?;
    let ratings = db.load_ratings_of_entry(&entry.id)?;
    Ok((entry, ratings))
}

#[cfg(test)]
mod tests {

    use super::super::tests::MockDb;
    use super::*;
    use uuid::Uuid;

    #[test]
    fn update_valid_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .title("foo")
            .description("bar")
            .image_url(Some("http://img"))
            .image_link_url(Some("http://imglink"))
            .license(Some("CC0-1.0"))
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : Some("img2".into()),
            image_link_url: old.image_link_url.clone(),
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let now = Timestamp::now();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        assert_eq!(mock_db.entries.borrow().len(), 1);
        let x = &mock_db.entries.borrow()[0];
        assert_eq!(
            "street",
            x.location
                .address
                .as_ref()
                .unwrap()
                .street
                .as_ref()
                .unwrap()
        );
        assert_eq!("bar", x.description);
        assert_eq!(2, x.version);
        assert!(x.created >= now);
        assert_eq!(None, x.archived);
        assert!(Uuid::parse_str(&x.id).is_ok());
        assert_eq!("https://www.img2/", x.image_url.as_ref().unwrap());
        assert_eq!("http://imglink/", x.image_link_url.as_ref().unwrap());
    }

    #[test]
    fn update_entry_with_invalid_version() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(3)
            .title("foo")
            .description("bar")
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 3,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::InvalidVersion => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 1);
    }

    #[test]
    fn update_non_existing_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 4,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::NotFound => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 0);
    }

    #[test]
    fn update_valid_entry_with_tags() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .tags(vec!["bio", "fair"])
            .license(Some("CC0-1.0"))
            .finish();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec!["vegan".into()],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        mock_db.tags = vec![Tag { id: "bio".into() }, Tag { id: "fair".into() }].into();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        let e = mock_db.get_entry(&id).unwrap();
        assert_eq!(None, e.archived);
        assert_eq!(e.tags, vec!["vegan"]);
        assert_eq!(mock_db.tags.borrow().len(), 3);
    }
}
<|endoftext|>


<commit_before>use crate::core::{
    prelude::*,
    util::{parse::parse_url_param, validate::Validate},
};

#[rustfmt::skip]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UpdateEntry {
    pub osm_node       : Option<u64>,
    pub version        : u64,
    pub title          : String,
    pub description    : String,
    pub lat            : f64,
    pub lng            : f64,
    pub street         : Option<String>,
    pub zip            : Option<String>,
    pub city           : Option<String>,
    pub country        : Option<String>,
    pub email          : Option<String>,
    pub telephone      : Option<String>,
    pub homepage       : Option<String>,
    pub categories     : Vec<String>,
    pub tags           : Vec<String>,
    pub image_url      : Option<String>,
    pub image_link_url : Option<String>,
}

pub struct Storable(Entry);

impl Storable {
    pub fn entry_id(&self) -> &str {
        &self.0.id
    }
}

pub fn prepare_updated_entry<D: Db>(db: &D, id: String, e: UpdateEntry) -> Result<Storable> {
    let old: Entry = db.get_entry(&id)?;
    if (old.version + 1) != e.version {
        return Err(Error::Repo(RepoError::InvalidVersion));
    }
    let UpdateEntry {
        version,
        title,
        description,
        lat,
        lng,
        street,
        zip,
        city,
        country,
        email,
        telephone,
        categories,
        tags,
        ..
    } = e;
    let pos = match MapPoint::try_from_lat_lng_deg(lat, lng) {
        None => return Err(ParameterError::InvalidPosition.into()),
        Some(pos) => pos,
    };
    let tags = super::prepare_tag_list(tags);
    super::check_and_count_owned_tags(db, &tags, None)?;
    let address = Address {
        street,
        zip,
        city,
        country,
    };
    let address = if address.is_empty() {
        None
    } else {
        Some(address)
    };
    let e = Entry {
        id,
        osm_node: None,
        created: Timestamp::now(),
        archived: None,
        version,
        title,
        description,
        location: Location { pos, address },
        contact: Some(Contact { email, telephone }),
        homepage: e.homepage.map(|ref url| parse_url_param(url)).transpose()?,
        categories,
        tags,
        license: old.license, // license is immutable
        image_url: e
            .image_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
        image_link_url: e
            .image_link_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
    };
    e.validate()?;
    Ok(Storable(e))
}

pub fn store_updated_entry<D: Db>(db: &D, s: Storable) -> Result<(Entry, Vec<Rating>)> {
    let Storable(entry) = s;
    debug!("Storing updated entry: {:?}", entry);
    for t in &entry.tags {
        db.create_tag_if_it_does_not_exist(&Tag { id: t.clone() })?;
    }
    db.update_entry(&entry)?;
    let ratings = db.load_ratings_of_entry(&entry.id)?;
    Ok((entry, ratings))
}

#[cfg(test)]
mod tests {

    use super::super::tests::MockDb;
    use super::*;
    use uuid::Uuid;

    #[test]
    fn update_valid_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .title("foo")
            .description("bar")
            .image_url(Some("http://img"))
            .image_link_url(Some("http://imglink"))
            .license(Some("CC0-1.0"))
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : Some("img2".into()),
            image_link_url: old.image_link_url.clone(),
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let now = Timestamp::now();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        assert_eq!(mock_db.entries.borrow().len(), 1);
        let x = &mock_db.entries.borrow()[0];
        assert_eq!(
            "street",
            x.location
                .address
                .as_ref()
                .unwrap()
                .street
                .as_ref()
                .unwrap()
        );
        assert_eq!("bar", x.description);
        assert_eq!(2, x.version);
        assert!(x.created >= now);
        assert_eq!(None, x.archived);
        assert!(Uuid::parse_str(&x.id).is_ok());
        assert_eq!("https://www.img2/", x.image_url.as_ref().unwrap());
        assert_eq!("http://imglink/", x.image_link_url.as_ref().unwrap());
    }

    #[test]
    fn update_entry_with_invalid_version() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(3)
            .title("foo")
            .description("bar")
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 3,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::InvalidVersion => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 1);
    }

    #[test]
    fn update_non_existing_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 4,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::NotFound => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 0);
    }

    #[test]
    fn update_valid_entry_with_tags() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .tags(vec!["bio", "fair"])
            .license(Some("CC0-1.0"))
            .finish();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec!["vegan".into()],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        mock_db.tags = vec![Tag { id: "bio".into() }, Tag { id: "fair".into() }].into();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        let e = mock_db.get_entry(&id).unwrap();
        assert_eq!(None, e.archived);
        assert_eq!(e.tags, vec!["vegan"]);
        assert_eq!(mock_db.tags.borrow().len(), 3);
    }
}
<commit_msg>Add TODO for GitHub issue #203<commit_after>use crate::core::{
    prelude::*,
    util::{parse::parse_url_param, validate::Validate},
};

#[rustfmt::skip]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UpdateEntry {
    pub osm_node       : Option<u64>,
    pub version        : u64,
    pub title          : String,
    pub description    : String,
    pub lat            : f64,
    pub lng            : f64,
    pub street         : Option<String>,
    pub zip            : Option<String>,
    pub city           : Option<String>,
    pub country        : Option<String>,
    pub email          : Option<String>,
    pub telephone      : Option<String>,
    pub homepage       : Option<String>,
    pub categories     : Vec<String>,
    pub tags           : Vec<String>,
    pub image_url      : Option<String>,
    pub image_link_url : Option<String>,
}

pub struct Storable(Entry);

impl Storable {
    pub fn entry_id(&self) -> &str {
        &self.0.id
    }
}

pub fn prepare_updated_entry<D: Db>(db: &D, id: String, e: UpdateEntry) -> Result<Storable> {
    let old: Entry = db.get_entry(&id)?;
    if (old.version + 1) != e.version {
        return Err(Error::Repo(RepoError::InvalidVersion));
    }
    let UpdateEntry {
        version,
        title,
        description,
        lat,
        lng,
        street,
        zip,
        city,
        country,
        email,
        telephone,
        categories,
        tags,
        ..
    } = e;
    let pos = match MapPoint::try_from_lat_lng_deg(lat, lng) {
        None => return Err(ParameterError::InvalidPosition.into()),
        Some(pos) => pos,
    };
    let tags = super::prepare_tag_list(tags);
    super::check_and_count_owned_tags(db, &tags, None)?;
    super::check_and_count_owned_tags(db, &tags, org.as_ref())?;
    // TODO: Ensure that no reserved tags are removed without authorization.
    // All existing reserved tags from other organizations must be preserved
    // when editing entries. Reserved tags that already exist should not be
    // considers during the check, because they must be preserved independent
    // of who is editing the entry.
    // GitHub issue: https://github.com/slowtec/openfairdb/issues/203
    let address = Address {
        street,
        zip,
        city,
        country,
    };
    let address = if address.is_empty() {
        None
    } else {
        Some(address)
    };
    let e = Entry {
        id,
        osm_node: None,
        created: Timestamp::now(),
        archived: None,
        version,
        title,
        description,
        location: Location { pos, address },
        contact: Some(Contact { email, telephone }),
        homepage: e.homepage.map(|ref url| parse_url_param(url)).transpose()?,
        categories,
        tags,
        license: old.license, // license is immutable
        image_url: e
            .image_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
        image_link_url: e
            .image_link_url
            .map(|ref url| parse_url_param(url))
            .transpose()?,
    };
    e.validate()?;
    Ok(Storable(e))
}

pub fn store_updated_entry<D: Db>(db: &D, s: Storable) -> Result<(Entry, Vec<Rating>)> {
    let Storable(entry) = s;
    debug!("Storing updated entry: {:?}", entry);
    for t in &entry.tags {
        db.create_tag_if_it_does_not_exist(&Tag { id: t.clone() })?;
    }
    db.update_entry(&entry)?;
    let ratings = db.load_ratings_of_entry(&entry.id)?;
    Ok((entry, ratings))
}

#[cfg(test)]
mod tests {

    use super::super::tests::MockDb;
    use super::*;
    use uuid::Uuid;

    #[test]
    fn update_valid_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .title("foo")
            .description("bar")
            .image_url(Some("http://img"))
            .image_link_url(Some("http://imglink"))
            .license(Some("CC0-1.0"))
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : Some("img2".into()),
            image_link_url: old.image_link_url.clone(),
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let now = Timestamp::now();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        assert_eq!(mock_db.entries.borrow().len(), 1);
        let x = &mock_db.entries.borrow()[0];
        assert_eq!(
            "street",
            x.location
                .address
                .as_ref()
                .unwrap()
                .street
                .as_ref()
                .unwrap()
        );
        assert_eq!("bar", x.description);
        assert_eq!(2, x.version);
        assert!(x.created >= now);
        assert_eq!(None, x.archived);
        assert!(Uuid::parse_str(&x.id).is_ok());
        assert_eq!("https://www.img2/", x.image_url.as_ref().unwrap());
        assert_eq!("http://imglink/", x.image_link_url.as_ref().unwrap());
    }

    #[test]
    fn update_entry_with_invalid_version() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(3)
            .title("foo")
            .description("bar")
            .finish();

        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 3,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::InvalidVersion => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 1);
    }

    #[test]
    fn update_non_existing_entry() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    : None,
            version     : 4,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec![],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![].into();
        let result = prepare_updated_entry(&mock_db, id.clone(), new);
        assert!(result.is_err());
        match result.err().unwrap() {
            Error::Repo(err) => match err {
                RepoError::NotFound => {}
                _ => {
                    panic!("invalid error type");
                }
            },
            _ => {
                panic!("invalid error type");
            }
        }
        assert_eq!(mock_db.entries.borrow().len(), 0);
    }

    #[test]
    fn update_valid_entry_with_tags() {
        let id = Uuid::new_v4().to_simple_ref().to_string();
        let old = Entry::build()
            .id(&id)
            .version(1)
            .tags(vec!["bio", "fair"])
            .license(Some("CC0-1.0"))
            .finish();
        #[rustfmt::skip]
        let new = UpdateEntry {
            osm_node    :  None,
            version     : 2,
            title       : "foo".into(),
            description : "bar".into(),
            lat         : 0.0,
            lng         : 0.0,
            street      : Some("street".into()),
            zip         : None,
            city        : None,
            country     : None,
            email       : None,
            telephone   : None,
            homepage    : None,
            categories  : vec![],
            tags        : vec!["vegan".into()],
            image_url     : None,
            image_link_url: None,
        };
        let mut mock_db = MockDb::default();
        mock_db.entries = vec![old].into();
        mock_db.tags = vec![Tag { id: "bio".into() }, Tag { id: "fair".into() }].into();
        let e = prepare_updated_entry(&mock_db, id.clone(), new).unwrap();
        assert!(store_updated_entry(&mock_db, e).is_ok());
        let e = mock_db.get_entry(&id).unwrap();
        assert_eq!(None, e.archived);
        assert_eq!(e.tags, vec!["vegan"]);
        assert_eq!(mock_db.tags.borrow().len(), 3);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fmt::{Show,Formatter};
use std::io::{UserDir};
use std::io::fs::{mkdir_recursive,rmdir_recursive};
use serialize::{Encodable,Encoder};
use url::Url;

use util::{CargoResult, ChainError, ProcessBuilder, process, human};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

macro_rules! git(
    ($config:expr, $($arg:expr),+) => (
        try!(git_inherit(&$config, process("git")$(.arg($arg))*))
    )
)

macro_rules! git_output(
    ($config:expr, $($arg:expr),*) => ({
        try!(git_output(&$config, process("git")$(.arg($arg))*))
    })
)

macro_rules! errln(
    ($($arg:tt)*) => (let _ = writeln!(::std::io::stdio::stderr(), $($arg)*))
)

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
#[deriving(PartialEq,Clone)]
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout {
    database: GitDatabase,
    location: Path,
    revision: GitRevision,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: GitDatabase,
    location: String,
    revision: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitCheckout {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            database: self.database.clone(),
            location: self.location.display().to_string(),
            revision: self.revision.to_string()
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        // Make sure we only rev-parse a success if the reference is actually
        // an object in the git database. Git will otherwise just verify that
        // it's a 40-length hex string (almost always true)
        let reference = format!("{}^{{object}}", reference.as_slice());
        Ok(GitRevision(git_output!(*path, "rev-parse", reference.as_slice())))
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        if into.exists() {
            try!(self.fetch_into(into));
        } else {
            try!(self.clone_into(into));
        }

        Ok(GitDatabase { remote: self.clone(), path: into.clone() })
    }

    pub fn db_at(&self, db_path: &Path) -> GitDatabase {
        GitDatabase { remote: self.clone(), path: db_path.clone() }
    }

    fn fetch_into(&self, path: &Path) -> CargoResult<()> {
        Ok(git!(*path, "fetch", "--force", "--quiet", "--tags",
                self.url.to_string(), "refs/heads/*:refs/heads/*"))
    }

    fn clone_into(&self, path: &Path) -> CargoResult<()> {
        let dirname = Path::new(path.dirname());

        try!(mkdir_recursive(path, UserDir));

        Ok(git!(dirname, "clone", self.url.to_string(), path, "--bare",
                "--no-hardlinks", "--quiet"))
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = try!(GitCheckout::clone_into(dest, self.clone(),
                                                    rev.clone()));

        match self.remote.rev_for(dest, "HEAD") {
            Ok(ref head) if rev == *head => {}
            _ => try!(checkout.fetch()),
        }

        try!(checkout.update_submodules());

        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        self.remote.rev_for(&self.path, reference)
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        git_output!(self.path, "rev-parse", "--verify", reference.as_slice());
        Ok(())
    }
}

impl GitCheckout {
    fn clone_into(into: &Path, database: GitDatabase,
                  revision: GitRevision) -> CargoResult<GitCheckout> {
        let checkout = GitCheckout {
            location: into.clone(),
            database: database,
            revision: revision,
        };

        // If the git checkout already exists, we don't need to clone it again
        if !checkout.location.join(".git").exists() {
            try!(checkout.clone_repo());
        }

        Ok(checkout)
    }

    fn get_source(&self) -> &Path {
        self.database.get_path()
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(&self) -> CargoResult<()> {
        let dirname = Path::new(self.location.dirname());

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}",
                          Path::new(self.location.dirname()).display()))
        }));

        if self.location.exists() {
            try!(rmdir_recursive(&self.location).chain_error(|| {
                human(format!("Couldn't rmdir {}",
                              Path::new(&self.location).display()))
            }));
        }

        git!(dirname, "clone", "--no-checkout", "--quiet",
             self.get_source(), &self.location);
        try!(self.reset());

        Ok(())
    }

    fn fetch(&self) -> CargoResult<()> {
        // In git 1.8, apparently --tags explicitly *only* fetches tags, it does
        // not fetch anything else. In git 1.9, however, git apparently fetches
        // everything when --tags is passed.
        //
        // This means that if we want to fetch everything we need to execute
        // both with and without --tags on 1.8 (apparently), and only with
        // --tags on 1.9. For simplicity, we execute with and without --tags for
        // all gits.
        //
        // FIXME: This is suspicious. I have been informed that, for example,
        //        bundler does not do this, yet bundler appears to work!
        //
        // And to continue the fun, git before 1.7.3 had the fun bug that if a
        // branch was tracking a remote, then `git fetch $url` doesn't work!
        //
        // For details, see
        // https://www.kernel.org/pub/software/scm/git/docs/RelNotes-1.7.3.txt
        //
        // In this case we just use `origin` here instead of the database path.
        git!(self.location, "fetch", "--force", "--quiet", "origin");
        git!(self.location, "fetch", "--force", "--quiet", "--tags", "origin");
        try!(self.reset());
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        Ok(git!(self.location, "reset", "-q", "--hard",
                self.revision.as_slice()))
    }

    fn update_submodules(&self) -> CargoResult<()> {
        Ok(git!(self.location, "submodule", "update", "--init",
                "--recursive", "--quiet"))
    }
}

fn git(path: &Path, cmd: ProcessBuilder) -> ProcessBuilder {
    debug!("Executing {} @ {}", cmd, path.display());

    cmd.cwd(path.clone())
}

fn git_inherit(path: &Path, cmd: ProcessBuilder) -> CargoResult<()> {
    let cmd = git(path, cmd);
    cmd.exec().chain_error(|| {
        human(format!("Executing {} failed", cmd))
    })
}

fn git_output(path: &Path, cmd: ProcessBuilder) -> CargoResult<String> {
    let cmd = git(path, cmd);
    let output = try!(cmd.exec_with_output().chain_error(||
        human(format!("Executing {} failed", cmd))));

    Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_string())
}

fn to_str(vec: &[u8]) -> String {
    String::from_utf8_lossy(vec).into_string()
}

<commit_msg>Take a different strategy than ^{object}<commit_after>use std::fmt;
use std::fmt::{Show,Formatter};
use std::io::{UserDir};
use std::io::fs::{mkdir_recursive,rmdir_recursive};
use serialize::{Encodable,Encoder};
use url::Url;

use util::{CargoResult, ChainError, ProcessBuilder, process, human};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            Master
        } else {
            Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            Master => "master",
            Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

macro_rules! git(
    ($config:expr, $($arg:expr),+) => (
        try!(git_inherit(&$config, process("git")$(.arg($arg))*))
    )
)

macro_rules! git_output(
    ($config:expr, $($arg:expr),*) => ({
        try!(git_output(&$config, process("git")$(.arg($arg))*))
    })
)

macro_rules! errln(
    ($($arg:tt)*) => (let _ = writeln!(::std::io::stdio::stderr(), $($arg)*))
)

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
#[deriving(PartialEq,Clone)]
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout {
    database: GitDatabase,
    location: Path,
    revision: GitRevision,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: GitDatabase,
    location: String,
    revision: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitCheckout {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            database: self.database.clone(),
            location: self.location.display().to_string(),
            revision: self.revision.to_string()
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        // We simultaneously want to transform the reference into a resolved
        // revision as well as verify that the reference itself is inside the
        // repository. Sadly for a 40-character SHA1 the call to `rev-parse`
        // will *always* return the same string with a 0 exit status, regardless
        // of whether it's present in the database.
        //
        // Later versions of git introduced a syntax for this query via
        // `$sha1^{object}`, but older versions of git do not support this. To
        // get around this limitation, we chop 40-character sha revisions to 39
        // characters to get an error'd exit status if the revision is indeed
        // not present.
        let mut reference = reference.as_slice();
        if reference.len() == 40 {
            reference = reference.slice_to(39);
        }
        Ok(GitRevision(git_output!(*path, "rev-parse", reference)))
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        if into.exists() {
            try!(self.fetch_into(into));
        } else {
            try!(self.clone_into(into));
        }

        Ok(GitDatabase { remote: self.clone(), path: into.clone() })
    }

    pub fn db_at(&self, db_path: &Path) -> GitDatabase {
        GitDatabase { remote: self.clone(), path: db_path.clone() }
    }

    fn fetch_into(&self, path: &Path) -> CargoResult<()> {
        Ok(git!(*path, "fetch", "--force", "--quiet", "--tags",
                self.url.to_string(), "refs/heads/*:refs/heads/*"))
    }

    fn clone_into(&self, path: &Path) -> CargoResult<()> {
        let dirname = Path::new(path.dirname());

        try!(mkdir_recursive(path, UserDir));

        Ok(git!(dirname, "clone", self.url.to_string(), path, "--bare",
                "--no-hardlinks", "--quiet"))
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = try!(GitCheckout::clone_into(dest, self.clone(),
                                                    rev.clone()));

        match self.remote.rev_for(dest, "HEAD") {
            Ok(ref head) if rev == *head => {}
            _ => try!(checkout.fetch()),
        }

        try!(checkout.update_submodules());

        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        self.remote.rev_for(&self.path, reference)
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        git_output!(self.path, "rev-parse", "--verify", reference.as_slice());
        Ok(())
    }
}

impl GitCheckout {
    fn clone_into(into: &Path, database: GitDatabase,
                  revision: GitRevision) -> CargoResult<GitCheckout> {
        let checkout = GitCheckout {
            location: into.clone(),
            database: database,
            revision: revision,
        };

        // If the git checkout already exists, we don't need to clone it again
        if !checkout.location.join(".git").exists() {
            try!(checkout.clone_repo());
        }

        Ok(checkout)
    }

    fn get_source(&self) -> &Path {
        self.database.get_path()
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(&self) -> CargoResult<()> {
        let dirname = Path::new(self.location.dirname());

        try!(mkdir_recursive(&dirname, UserDir).chain_error(|| {
            human(format!("Couldn't mkdir {}",
                          Path::new(self.location.dirname()).display()))
        }));

        if self.location.exists() {
            try!(rmdir_recursive(&self.location).chain_error(|| {
                human(format!("Couldn't rmdir {}",
                              Path::new(&self.location).display()))
            }));
        }

        git!(dirname, "clone", "--no-checkout", "--quiet",
             self.get_source(), &self.location);
        try!(self.reset());

        Ok(())
    }

    fn fetch(&self) -> CargoResult<()> {
        // In git 1.8, apparently --tags explicitly *only* fetches tags, it does
        // not fetch anything else. In git 1.9, however, git apparently fetches
        // everything when --tags is passed.
        //
        // This means that if we want to fetch everything we need to execute
        // both with and without --tags on 1.8 (apparently), and only with
        // --tags on 1.9. For simplicity, we execute with and without --tags for
        // all gits.
        //
        // FIXME: This is suspicious. I have been informed that, for example,
        //        bundler does not do this, yet bundler appears to work!
        //
        // And to continue the fun, git before 1.7.3 had the fun bug that if a
        // branch was tracking a remote, then `git fetch $url` doesn't work!
        //
        // For details, see
        // https://www.kernel.org/pub/software/scm/git/docs/RelNotes-1.7.3.txt
        //
        // In this case we just use `origin` here instead of the database path.
        git!(self.location, "fetch", "--force", "--quiet", "origin");
        git!(self.location, "fetch", "--force", "--quiet", "--tags", "origin");
        try!(self.reset());
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        Ok(git!(self.location, "reset", "-q", "--hard",
                self.revision.as_slice()))
    }

    fn update_submodules(&self) -> CargoResult<()> {
        Ok(git!(self.location, "submodule", "update", "--init",
                "--recursive", "--quiet"))
    }
}

fn git(path: &Path, cmd: ProcessBuilder) -> ProcessBuilder {
    debug!("Executing {} @ {}", cmd, path.display());

    cmd.cwd(path.clone())
}

fn git_inherit(path: &Path, cmd: ProcessBuilder) -> CargoResult<()> {
    let cmd = git(path, cmd);
    cmd.exec().chain_error(|| {
        human(format!("Executing {} failed", cmd))
    })
}

fn git_output(path: &Path, cmd: ProcessBuilder) -> CargoResult<String> {
    let cmd = git(path, cmd);
    let output = try!(cmd.exec_with_output().chain_error(||
        human(format!("Executing {} failed", cmd))));

    Ok(to_str(output.output.as_slice()).as_slice().trim_right().to_string())
}

fn to_str(vec: &[u8]) -> String {
    String::from_utf8_lossy(vec).into_string()
}

<|endoftext|>


<commit_before>//   Copyright 2015 Colin Sherratt
//
//   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.

extern crate "snowmew-core" as core;
extern crate "snowmew-position" as position;
extern crate "snowmew-graphics" as graphics;
extern crate "snowmew-render" as render;
extern crate "snowmew-input" as input;

extern crate wire;
extern crate bchannel;
extern crate bincode;
extern crate "rustc-serialize" as rustc_serialize;

use core::common::{Common, CommonData};
use position::{Positions, PositionData};
use graphics::{Graphics, GraphicsData};
use render::{Renderable, RenderData};
use input::{IoState, GetIoState};

use std::sync::mpsc;
use std::thread::Thread;
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::io::net::ip::ToSocketAddr;
use wire::SizeLimit;
use wire::tcp::OutTcpStream;
use rustc_serialize::{Decodable, Encodable};

use core::game::Game;

#[derive(RustcEncodable, RustcDecodable)]
pub enum ServerMessage<T, E> {
    Image(T),
    Event(E)
}

#[derive(RustcEncodable, RustcDecodable)]
pub enum ClientMessage<E> {
    Join,
    Event(E)
}

pub struct Server<Game, GameData> {
    game: Game,
    data: GameData
}

unsafe impl<T:Send, E:Send, CE:Send> Send for ServerClient<T, E, CE> {}

struct ServerClient<T, E, CE> {
    joined: bool,
    to_client: mpsc::Sender<ServerMessage<T, CE>>,
    from_client: bchannel::Receiver<ClientMessage<E>, bincode::DecodingError>
}

impl<G, GD:Clone+Send+Decodable+Encodable> Server<G, GD> {
    pub fn new(game: G, data: GD) -> Server<G, GD> {
        Server {
            game: game,
            data: data
        }
    }

    pub fn serve<E: Send+Encodable+Clone+Decodable+FromIterator<CE>,
                 CE: Send+Encodable+Clone+Decodable,
                 A>(mut self, addr: A)
        where G: Game<GD, E>, A: ToSocketAddr {

        let (listener, _) = wire::listen_tcp(addr).unwrap();

        let (tx, rx): (mpsc::Sender<ServerClient<GD, CE, E>>,
                       mpsc::Receiver<ServerClient<GD, CE, E>>) = mpsc::channel();
        Thread::spawn(move || {
            let (read_limit, write_limit) = (SizeLimit::Infinite, SizeLimit::Infinite);

            for connection in listener.into_blocking_iter() {
                let (i, o) = wire::upgrade_tcp(connection, read_limit, write_limit);

                let (to_client_tx, to_client_rx) = mpsc::channel();

                // client thread
                Thread::spawn(move || {
                    let mut o = o;
                    for msg in to_client_rx.iter() {
                        o.send(&msg);
                    }
                });

                let client = ServerClient {
                    joined: false,
                    to_client: to_client_tx,
                    from_client: i
                };

                tx.send(client);
            }
        });

        let mut clients = Vec::new();

        if let Ok(mut res) = rx.recv() {
            res.to_client.send(ServerMessage::Image(self.data.clone()));
            clients.push(res);
        }

        loop {
            while let Ok(mut res) = rx.try_recv() {
                res.to_client.send(ServerMessage::Image(self.data.clone()));
                clients.push(res);
            }

            let mut events: Vec<CE> = clients.iter_mut().map(|c| {
                if c.joined {
                    let e = c.from_client.recv_block().expect("client sucks man");
                    Some(match e {
                        ClientMessage::Join => panic!("invalid message"),
                        ClientMessage::Event(e) => e
                    })
                } else {
                    match c.from_client.recv() {
                        Some(ClientMessage::Join) => c.joined = true,
                        _ => ()
                    }
                    None
                }
            }).filter(|x| x.is_some()).map(|x| x.unwrap()).collect();

            println!("events.len={}", events.len());

            if events.len() == 0 { continue }

            let event: E = events.drain().collect();

            for c in clients.iter_mut() {
                c.to_client.send(ServerMessage::Event(event.clone()));
            }
            self.data = self.game.step(event, self.data);
        }
    }
}

#[derive(Clone)]
pub struct ClientState<T, E> {
    joined: bool,
    predict_frame: u32,
    predict: T,
    predict_delta: Vec<(u32, E)>,
    server_frame: u32,
    server: T
}

impl<T, E> Deref for ClientState<T, E> {
    type Target = T;

    fn deref<'a>(&'a self) -> &'a T {
        &self.predict
    }
}

impl<T, E> DerefMut for ClientState<T, E> {
    fn deref_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.predict
    }
}

impl<E, T: Common> Common for ClientState<T, E> {
    fn get_common<'a>(&'a self) -> &'a CommonData { self.predict.get_common() }
    fn get_common_mut<'a>(&'a mut self) -> &'a mut CommonData { self.predict.get_common_mut() }
}

impl<E, T: Positions> Positions for ClientState<T, E> {
    fn get_position<'a>(&'a self) -> &'a PositionData { self.predict.get_position() }
    fn get_position_mut<'a>(&'a mut self) -> &'a mut PositionData { self.predict.get_position_mut() }
}

impl<E, T: Graphics> Graphics for ClientState<T, E> {
    fn get_graphics<'a>(&'a self) -> &'a GraphicsData { self.predict.get_graphics() }
    fn get_graphics_mut<'a>(&'a mut self) -> &'a mut GraphicsData { self.predict.get_graphics_mut() }
}

impl<E, T: Renderable> Renderable for ClientState<T, E> {
    fn get_render_data<'a>(&'a self) -> &'a RenderData { self.predict.get_render_data() }
    fn get_render_data_mut<'a>(&'a mut self) -> &'a mut RenderData { self.predict.get_render_data_mut() }
}

impl<E, T: GetIoState> GetIoState for ClientState<T, E> {
    fn get_io_state<'a>(&'a self) -> &'a IoState { self.predict.get_io_state() }
    fn get_io_state_mut<'a>(&'a mut self) -> &'a mut IoState { self.predict.get_io_state_mut() }
}

pub struct Client<G, T, SE, CE> {
    game: G,
    to_server: OutTcpStream<ClientMessage<CE>>,
    from_server: bchannel::Receiver<ServerMessage<T, SE>, bincode::DecodingError>
}

impl<Game, GameState, ServerEvent, ClientEvent> Client<Game, GameState, ServerEvent, ClientEvent>
    where GameState: Send+Encodable+Decodable+Clone,
          ServerEvent: Send+Encodable+Decodable+Clone,
          ClientEvent: Send+Encodable+Decodable+Clone
     {

    pub fn new<A>(game: Game, addr: A) -> Client<Game, GameState, ServerEvent, ClientEvent>
        where A: ToSocketAddr {
        let (o, i) = wire::connect_tcp(addr, SizeLimit::Infinite, SizeLimit::Infinite).unwrap();

        Client {
            game: game,
            to_server: i,
            from_server: o
        }
    }

    pub fn gamedata(&mut self) -> ClientState<GameState, ClientEvent> {
        match self.from_server.recv_block().expect("failed to get state") {
            ServerMessage::Image(state) => {
                println!("download done");
                ClientState {
                    joined: false,
                    predict_frame: 0,
                    predict: state.clone(),
                    predict_delta: Vec::new(),
                    server_frame: 0,
                    server: state.clone()
                }
            },
            _ => panic!("expected game data")
        }
    }
}

impl<G: Game<T, SE>,
     T:Send+Encodable+Decodable+Clone,
     SE:Send+Encodable+Decodable+Clone+FromIterator<CE>,
     CE:Send+Encodable+Decodable+Clone>
    Game<ClientState<T, CE>, CE> for Client<G, T, SE, CE> {

    fn step(&mut self, event: CE, mut gd: ClientState<T, CE>) -> ClientState<T, CE> {
        // ack the server at start
        if !gd.joined {
            self.to_server.send(&ClientMessage::Join);
            gd.joined = true;
        }

        let mut frame = gd.predict.clone();
        let mut index = gd.predict_frame;

        loop {
            match self.from_server.recv() {
                Some(ServerMessage::Image(_)) => panic!("unexpected game data"),
                Some(ServerMessage::Event(e)) => {
                    gd.server = self.game.step(e, gd.server);
                    gd.server_frame += 1;
                    frame = gd.server.clone();
                    index = gd.server_frame;
                },
                None => break
            }
        }

        self.to_server.send(&ClientMessage::Event(event.clone()));
        gd.predict_frame += 1;
        gd.predict_delta.push((gd.predict_frame, event));

        let mut predict = Vec::new();
        for (idx, e) in gd.predict_delta.drain() {
            if idx > index {
                println!("{} > {} > {}", idx, index, gd.server_frame);
                frame = self.game.step([e.clone()].iter().map(|x| x.clone()).collect(), frame);
                index = idx;
            }

            if idx > gd.server_frame {
                predict.push((idx, e.clone()));
            }
        }

        gd.predict = frame;
        gd.predict_delta = predict;
        gd.predict_frame = index;
        gd
    }
}

<commit_msg>Added back networking<commit_after>//   Copyright 2015 Colin Sherratt
//
//   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.

extern crate "snowmew-core" as core;
extern crate "snowmew-position" as position;
extern crate "snowmew-graphics" as graphics;
extern crate "snowmew-render" as render;
extern crate "snowmew-input" as input;

extern crate wire;
extern crate bchannel;
extern crate bincode;
extern crate "rustc-serialize" as rustc_serialize;

use core::common::{Common, CommonData};
use position::{Positions, PositionData};
use graphics::{Graphics, GraphicsData};
use render::{Renderable, RenderData};
use input::{IoState, GetIoState};

use std::sync::mpsc;
use std::thread::Thread;
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::old_io::net::ip::ToSocketAddr;
use wire::SizeLimit;
use wire::tcp::OutTcpStream;
use rustc_serialize::{Decodable, Encodable};

use core::game::Game;

#[derive(RustcEncodable, RustcDecodable)]
pub enum ServerMessage<T, E> {
    Image(T),
    Event(E)
}

#[derive(RustcEncodable, RustcDecodable)]
pub enum ClientMessage<E> {
    Join,
    Event(E)
}

pub struct Server<Game, GameData> {
    game: Game,
    data: GameData
}

unsafe impl<T:Send, E:Send, CE:Send> Send for ServerClient<T, E, CE> {}

struct ServerClient<T, E, CE> {
    joined: bool,
    to_client: mpsc::Sender<ServerMessage<T, CE>>,
    from_client: bchannel::Receiver<ClientMessage<E>, bincode::DecodingError>
}

impl<G, GD:Clone+Send+Decodable+Encodable> Server<G, GD> {
    pub fn new(game: G, data: GD) -> Server<G, GD> {
        Server {
            game: game,
            data: data
        }
    }

    pub fn serve<E: Send+Encodable+Clone+Decodable+FromIterator<CE>,
                 CE: Send+Encodable+Clone+Decodable,
                 A>(mut self, addr: A)
        where G: Game<GD, E>, A: ToSocketAddr {

        let (listener, _) = wire::listen_tcp(addr).unwrap();

        let (tx, rx): (mpsc::Sender<ServerClient<GD, CE, E>>,
                       mpsc::Receiver<ServerClient<GD, CE, E>>) = mpsc::channel();
        Thread::spawn(move || {
            let (read_limit, write_limit) = (SizeLimit::Infinite, SizeLimit::Infinite);

            for connection in listener.into_blocking_iter() {
                let (i, o) = wire::upgrade_tcp(connection, read_limit, write_limit);

                let (to_client_tx, to_client_rx) = mpsc::channel();

                // client thread
                Thread::spawn(move || {
                    let mut o = o;
                    for msg in to_client_rx.iter() {
                        o.send(&msg);
                    }
                });

                let client = ServerClient {
                    joined: false,
                    to_client: to_client_tx,
                    from_client: i
                };

                tx.send(client);
            }
        });

        let mut clients = Vec::new();

        if let Ok(mut res) = rx.recv() {
            res.to_client.send(ServerMessage::Image(self.data.clone()));
            clients.push(res);
        }

        loop {
            while let Ok(mut res) = rx.try_recv() {
                res.to_client.send(ServerMessage::Image(self.data.clone()));
                clients.push(res);
            }

            let mut events: Vec<CE> = clients.iter_mut().map(|c| {
                if c.joined {
                    let e = c.from_client.recv_block().expect("client sucks man");
                    Some(match e {
                        ClientMessage::Join => panic!("invalid message"),
                        ClientMessage::Event(e) => e
                    })
                } else {
                    match c.from_client.recv() {
                        Some(ClientMessage::Join) => c.joined = true,
                        _ => ()
                    }
                    None
                }
            }).filter(|x| x.is_some()).map(|x| x.unwrap()).collect();

            println!("events.len={}", events.len());

            if events.len() == 0 { continue }

            let event: E = events.drain().collect();

            for c in clients.iter_mut() {
                c.to_client.send(ServerMessage::Event(event.clone()));
            }
            self.data = self.game.step(event, self.data);
        }
    }
}

#[derive(Clone)]
pub struct ClientState<T, E> {
    joined: bool,
    predict_frame: u32,
    predict: T,
    predict_delta: Vec<(u32, E)>,
    server_frame: u32,
    server: T
}

impl<T, E> Deref for ClientState<T, E> {
    type Target = T;

    fn deref<'a>(&'a self) -> &'a T {
        &self.predict
    }
}

impl<T, E> DerefMut for ClientState<T, E> {
    fn deref_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.predict
    }
}

impl<E, T: Common> Common for ClientState<T, E> {
    fn get_common<'a>(&'a self) -> &'a CommonData { self.predict.get_common() }
    fn get_common_mut<'a>(&'a mut self) -> &'a mut CommonData { self.predict.get_common_mut() }
}

impl<E, T: Positions> Positions for ClientState<T, E> {
    fn get_position<'a>(&'a self) -> &'a PositionData { self.predict.get_position() }
    fn get_position_mut<'a>(&'a mut self) -> &'a mut PositionData { self.predict.get_position_mut() }
}

impl<E, T: Graphics> Graphics for ClientState<T, E> {
    fn get_graphics<'a>(&'a self) -> &'a GraphicsData { self.predict.get_graphics() }
    fn get_graphics_mut<'a>(&'a mut self) -> &'a mut GraphicsData { self.predict.get_graphics_mut() }
}

impl<E, T: Renderable> Renderable for ClientState<T, E> {
    fn get_render_data<'a>(&'a self) -> &'a RenderData { self.predict.get_render_data() }
    fn get_render_data_mut<'a>(&'a mut self) -> &'a mut RenderData { self.predict.get_render_data_mut() }
}

impl<E, T: GetIoState> GetIoState for ClientState<T, E> {
    fn get_io_state<'a>(&'a self) -> &'a IoState { self.predict.get_io_state() }
    fn get_io_state_mut<'a>(&'a mut self) -> &'a mut IoState { self.predict.get_io_state_mut() }
}

pub struct Client<G, T, SE, CE> {
    game: G,
    to_server: OutTcpStream<ClientMessage<CE>>,
    from_server: bchannel::Receiver<ServerMessage<T, SE>, bincode::DecodingError>
}

impl<Game, GameState, ServerEvent, ClientEvent> Client<Game, GameState, ServerEvent, ClientEvent>
    where GameState: Send+Encodable+Decodable+Clone,
          ServerEvent: Send+Encodable+Decodable+Clone,
          ClientEvent: Send+Encodable+Decodable+Clone
     {

    pub fn new<A>(game: Game, addr: A) -> Client<Game, GameState, ServerEvent, ClientEvent>
        where A: ToSocketAddr {
        let (o, i) = wire::connect_tcp(addr, SizeLimit::Infinite, SizeLimit::Infinite).unwrap();

        Client {
            game: game,
            to_server: i,
            from_server: o
        }
    }

    pub fn gamedata(&mut self) -> ClientState<GameState, ClientEvent> {
        match self.from_server.recv_block().expect("failed to get state") {
            ServerMessage::Image(state) => {
                println!("download done");
                ClientState {
                    joined: false,
                    predict_frame: 0,
                    predict: state.clone(),
                    predict_delta: Vec::new(),
                    server_frame: 0,
                    server: state.clone()
                }
            },
            _ => panic!("expected game data")
        }
    }
}

impl<G: Game<T, SE>,
     T:Send+Encodable+Decodable+Clone,
     SE:Send+Encodable+Decodable+Clone+FromIterator<CE>,
     CE:Send+Encodable+Decodable+Clone>
    Game<ClientState<T, CE>, CE> for Client<G, T, SE, CE> {

    fn step(&mut self, event: CE, mut gd: ClientState<T, CE>) -> ClientState<T, CE> {
        // ack the server at start
        if !gd.joined {
            self.to_server.send(&ClientMessage::Join);
            gd.joined = true;
        }

        let mut frame = gd.predict.clone();
        let mut index = gd.predict_frame;

        loop {
            match self.from_server.recv() {
                Some(ServerMessage::Image(_)) => panic!("unexpected game data"),
                Some(ServerMessage::Event(e)) => {
                    gd.server = self.game.step(e, gd.server);
                    gd.server_frame += 1;
                    frame = gd.server.clone();
                    index = gd.server_frame;
                },
                None => break
            }
        }

        self.to_server.send(&ClientMessage::Event(event.clone()));
        gd.predict_frame += 1;
        gd.predict_delta.push((gd.predict_frame, event));

        let mut predict = Vec::new();
        for (idx, e) in gd.predict_delta.drain() {
            if idx > index {
                println!("{} > {} > {}", idx, index, gd.server_frame);
                frame = self.game.step([e.clone()].iter().map(|x| x.clone()).collect(), frame);
                index = idx;
            }

            if idx > gd.server_frame {
                predict.push((idx, e.clone()));
            }
        }

        gd.predict = frame;
        gd.predict_delta = predict;
        gd.predict_frame = index;
        gd
    }
}

<|endoftext|>