<commit_before>#![allow(unsafe_code)]

use std::convert::TryFrom;
use std::ptr;
use std::sync::atomic::{AtomicPtr, AtomicU64, AtomicUsize, Ordering};

use crate::{
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { std::mem::transmute([0_u64; MAX_QUEUE_ITEMS]) },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::SeqCst);

            if offset < MAX_QUEUE_ITEMS {
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item.0, Ordering::SeqCst);
                }
                return filled;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::default()));
                debug_delay();
                let prev =
                    self.writing.compare_and_swap(head, new, Ordering::SeqCst);
                if prev != head {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::SeqCst);
                    debug_delay();
                    ret = self.full_list.compare_and_swap(
                        full_list_ptr,
                        head,
                        Ordering::SeqCst,
                    );
                    ret != full_list_ptr
                } {
                    full_list_ptr = ret;
                }
                filled = true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::SeqCst);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::SeqCst);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop = unsafe { Box::from_raw(self.current_block) };
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess(next));
        }

        None
    }
}

#[derive(Clone, Copy)]
struct CacheAccess(u64);

impl CacheAccess {
    fn new(pid: PageId, sz: u64) -> CacheAccess {
        let rounded_up_power_of_2 =
            u64::from(sz.next_power_of_two().trailing_zeros());

        assert!(rounded_up_power_of_2 < 256);

        CacheAccess(pid | (rounded_up_power_of_2 << 56))
    }

    const fn decompose(self) -> (PageId, u64) {
        let sz = 1 << (self.0 >> 56);
        let pid = self.0 << 8 >> 8;
        (pid, sz)
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

unsafe impl Sync for Lru {}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: u64) -> Self {
        assert!(
            cache_capacity >= 256,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS as u64;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: u64,
        guard: &Guard,
    ) -> Vec<PageId> {
        let mut ret = vec![];
        let shards = self.shards.len() as u64;
        let (shard_idx, item_pos) = (id % shards, id / shards);
        let (stack, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let filled = stack.push(CacheAccess::new(item_pos, item_size));

        if filled {
            // only try to acquire this if
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = stack.take(guard);
                for item in accesses {
                    let (item_pos, item_size) = item.decompose();
                    let to_evict =
                        shard.accessed(safe_usize(item_pos), item_size);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item = (pos * shards) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Clone)]
struct Entry {
    ptr: *mut Node,
    size: u64,
}

impl Default for Entry {
    fn default() -> Self {
        Self { ptr: ptr::null_mut(), size: 0 }
    }
}

struct Shard {
    list: DoublyLinkedList,
    entries: Vec<Entry>,
    capacity: u64,
    size: u64,
}

impl Shard {
    fn new(capacity: u64) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            list: DoublyLinkedList::default(),
            entries: vec![],
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, pos: usize, size: u64) -> Vec<PageId> {
        if pos >= self.entries.len() {
            self.entries.resize(pos + 1, Entry::default());
        }

        {
            let entry = &mut self.entries[pos];

            self.size -= entry.size;
            entry.size = size;
            self.size += size;

            if entry.ptr.is_null() {
                entry.ptr = self.list.push_head(PageId::try_from(pos).unwrap());
            } else {
                entry.ptr = self.list.promote(entry.ptr);
            }
        }

        let mut to_evict = vec![];
        while self.size > self.capacity {
            if self.list.len() == 1 {
                // don't evict what we just added
                break;
            }

            let min_pid = self.list.pop_tail().unwrap();
            let min_pid_idx = safe_usize(min_pid);

            self.entries[min_pid_idx].ptr = ptr::null_mut();

            to_evict.push(min_pid);

            self.size -= self.entries[min_pid_idx].size;
            self.entries[min_pid_idx].size = 0;
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}
<commit_msg>Add smoke test for Lru<commit_after>#![allow(unsafe_code)]

use std::convert::TryFrom;
use std::ptr;
use std::sync::atomic::{AtomicPtr, AtomicU64, AtomicUsize, Ordering};

use crate::{
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { std::mem::transmute([0_u64; MAX_QUEUE_ITEMS]) },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::SeqCst);

            if offset < MAX_QUEUE_ITEMS {
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item.0, Ordering::SeqCst);
                }
                return filled;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::default()));
                debug_delay();
                let prev =
                    self.writing.compare_and_swap(head, new, Ordering::SeqCst);
                if prev != head {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::SeqCst);
                    debug_delay();
                    ret = self.full_list.compare_and_swap(
                        full_list_ptr,
                        head,
                        Ordering::SeqCst,
                    );
                    ret != full_list_ptr
                } {
                    full_list_ptr = ret;
                }
                filled = true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::SeqCst);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::SeqCst);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop = unsafe { Box::from_raw(self.current_block) };
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess(next));
        }

        None
    }
}

#[derive(Clone, Copy)]
struct CacheAccess(u64);

impl CacheAccess {
    fn new(pid: PageId, sz: u64) -> CacheAccess {
        let rounded_up_power_of_2 =
            u64::from(sz.next_power_of_two().trailing_zeros());

        assert!(rounded_up_power_of_2 < 256);

        CacheAccess(pid | (rounded_up_power_of_2 << 56))
    }

    const fn decompose(self) -> (PageId, u64) {
        let sz = 1 << (self.0 >> 56);
        let pid = self.0 << 8 >> 8;
        (pid, sz)
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

unsafe impl Sync for Lru {}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: u64) -> Self {
        assert!(
            cache_capacity >= 256,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS as u64;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: u64,
        guard: &Guard,
    ) -> Vec<PageId> {
        let mut ret = vec![];
        let shards = self.shards.len() as u64;
        let (shard_idx, item_pos) = (id % shards, id / shards);
        let (stack, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let filled = stack.push(CacheAccess::new(item_pos, item_size));

        if filled {
            // only try to acquire this if
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = stack.take(guard);
                for item in accesses {
                    let (item_pos, item_size) = item.decompose();
                    let to_evict =
                        shard.accessed(safe_usize(item_pos), item_size);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item = (pos * shards) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Clone)]
struct Entry {
    ptr: *mut Node,
    size: u64,
}

impl Default for Entry {
    fn default() -> Self {
        Self { ptr: ptr::null_mut(), size: 0 }
    }
}

struct Shard {
    list: DoublyLinkedList,
    entries: Vec<Entry>,
    capacity: u64,
    size: u64,
}

impl Shard {
    fn new(capacity: u64) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            list: DoublyLinkedList::default(),
            entries: vec![],
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, pos: usize, size: u64) -> Vec<PageId> {
        if pos >= self.entries.len() {
            self.entries.resize(pos + 1, Entry::default());
        }

        {
            let entry = &mut self.entries[pos];

            self.size -= entry.size;
            entry.size = size;
            self.size += size;

            if entry.ptr.is_null() {
                entry.ptr = self.list.push_head(PageId::try_from(pos).unwrap());
            } else {
                entry.ptr = self.list.promote(entry.ptr);
            }
        }

        let mut to_evict = vec![];
        while self.size > self.capacity {
            if self.list.len() == 1 {
                // don't evict what we just added
                break;
            }

            let min_pid = self.list.pop_tail().unwrap();
            let min_pid_idx = safe_usize(min_pid);

            self.entries[min_pid_idx].ptr = ptr::null_mut();

            to_evict.push(min_pid);

            self.size -= self.entries[min_pid_idx].size;
            self.entries[min_pid_idx].size = 0;
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}

#[test]
fn lru_smoke_test() {
    use crate::pin;

    let lru = Lru::new(256);
    for i in 0..1000 {
        let guard = pin();
        lru.accessed(i, 16, &guard);
    }
}
<|endoftext|>


<commit_before>#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2
#![allow(non_snake_case)]

// mod api;
// mod cors;
// mod files;

extern crate actix;
extern crate actix_web;
extern crate env_logger;

extern crate postgres;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate dotenv;
use dotenv::dotenv;
use std::env;

use actix_web::{http::Method, middleware, server, App, FromRequest, HttpRequest, HttpResponse};

extern crate futures;
use futures::future::{result, FutureResult};

extern crate typed_html;
extern crate typed_html_macros;

use typed_html::elements::FlowContent;
use typed_html::types::Metadata;
use typed_html::{dom::DOMTree, html, text, OutputType};

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

fn index(_req: &HttpRequest) -> &'static str {
    "Hello world"
}

use std::path;
use std::process::Command;

#[derive(Deserialize)]
struct TemplateParams {
    ssr: bool,
}

fn template(req: &HttpRequest) -> FutureResult<HttpResponse, actix_web::error::Error> {
    let params = actix_web::Path::<TemplateParams>::extract(req).unwrap();
    let ssr = params.ssr;
    let s2 = getStr();
    let s: String = if ssr { s2.to_owned() } else { "".to_owned() };
    let context = TemplateContext {
        parent: "index".to_owned(),
        name: "Roman".to_owned(),
        content: s.to_owned(),
        items: vec!["One", "Two", "Three"]
            .iter()
            .map(|s| s.to_string())
            .collect(),
    };

    let doc: DOMTree<String> = doc(
        html!(

"Hello Kitty"

                    "She is not a cat. She is a human girl."

                { (0..3).map(|_| html!(

                        "Her name is Kitty White."

                )) }

                    "We still don't know how she eats."

"Hello Kitty"

                "DYNAMIC_CONTENT"

        ),
        context,
    );
    let mut doc_str = "<!doctype html>".to_owned() + &doc.to_string();

    // https://users.rust-lang.org/t/how-to-create-a-macro-from-dynamic-content/5079
    doc_str = doc_str.replacen("DYNAMIC_CONTENT", &s2, 1);

    result(Ok(HttpResponse::Ok()
        .content_type("text/html")
        .body(doc_str)))
}

fn doc<T: OutputType + 'static>(
    tree: Box<dyn FlowContent<T>>,
    context: TemplateContext,
) -> DOMTree<T> {
    let TemplateContext { name, items, .. } = context;
    html!(

                <title>"Hello Kitty"</title>
                <meta charset="utf-8" />
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <link rel="icon" href="/favicon.ico?v=1" />
                <link href="/styles/normalize.css" rel="stylesheet" type="text/css" media="all" />
                <link href="/styles/style.css" rel="stylesheet" type="text/css" media="all" />
                <base href="/"></base>
                <meta name=Metadata::Author content="Not Sanrio Co., Ltd" />

{text!("{}", name)}

"Here are your items:"

                   {
                    items.iter().map(|item| html!(

{text!("{}", item)}

                    ))
                   }

                { tree }

"Try going to ""/hello/YourName"

    )
}

use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;
extern crate time;

fn getStr() -> String {
    fs::copy(
        "../client/src/elm/Main.elm",
        "../client/src/elm/Main.elm.bak",
    )
    .expect("file not copied");
    let stdout: Vec<u8>;
    {
        let mut main_file = OpenOptions::new()
            .append(true)
            .open("../client/src/elm/Main.elm")
            .expect("file not opened");

        let str1 = format!(
            r#"
view : Html Msg
view =
    viewWithModel <|
        Model [] HomeRoute <|
            Flags "" {time} ""
"#,
            time = time::get_time().sec
        );

        main_file
            .write(str1.as_bytes())
            .expect("file content not saved");

        stdout = Command::new("node")
            .current_dir(&path::Path::new("../client"))
            .arg("./node_modules/elm-static-html/index.js")
            .arg("-f")
            .arg("src/elm/Main.elm")
            .output()
            .expect("elm-static-html command failed to start")
            .stdout;
    }
    fs::rename(
        "../client/src/elm/Main.elm.bak",
        "../client/src/elm/Main.elm",
    )
    .expect("file not renamed");

    String::from_utf8_lossy(&*stdout)
        .lines()
        .skip_while(|x| x.find("id=\"content\"").is_none())
        .collect()
}

// #[error(404)]
// fn not_found(req: &Request) -> CORS<Template> {
//     let mut map = std::collections::HashMap::new();
//     map.insert("path", req.uri().as_str());
//     CORS::any(Template::render("error/404", &map))
// }

fn main() {
    dotenv().ok();
    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let sys = actix::System::new("hello-world");

    server::new(|| {
        App::new()
            .middleware(middleware::Logger::default())
            .resource("/", |r| r.f(index))
            .resource("/template/{ssr}", |r| r.method(Method::GET).a(template))
    })
    .bind("127.0.0.1:8080")
    .unwrap()
    .start();

    println!("Starting http server: 127.0.0.1:8080");

    let _ = sys.run();

    // println!("{:?}", env::var("data_server").unwrap());
    // rocket::ignite()
    //     .mount(
    //         "/",
    //         routes![
    //             template,
    //             files::favicon,
    //             files::index,
    //             files::redirect_to_index,
    //             files::js,
    //             files::styles,
    //             files::styles_with_query,
    //             files::images
    //         ],
    //     )
    //     .mount(
    //         "/api",
    //         routes![
    //             api::user_information,
    //             api::data_sets,
    //             api::data_set,
    //             api::data_set_category,
    //             api::data_sets_categories,
    //             api::data_sets_new
    //         ],
    //     )
    //     .catch(errors![not_found])
    //     .launch();
}
<commit_msg>Indent multiline string<commit_after>#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2
#![allow(non_snake_case)]

// mod api;
// mod cors;
// mod files;

extern crate actix;
extern crate actix_web;
extern crate env_logger;

extern crate postgres;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate dotenv;
use dotenv::dotenv;
use std::env;

use actix_web::{http::Method, middleware, server, App, FromRequest, HttpRequest, HttpResponse};

extern crate futures;
use futures::future::{result, FutureResult};

extern crate typed_html;
extern crate typed_html_macros;

use typed_html::elements::FlowContent;
use typed_html::types::Metadata;
use typed_html::{dom::DOMTree, html, text, OutputType};

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

fn index(_req: &HttpRequest) -> &'static str {
    "Hello world"
}

use std::path;
use std::process::Command;

#[derive(Deserialize)]
struct TemplateParams {
    ssr: bool,
}

fn template(req: &HttpRequest) -> FutureResult<HttpResponse, actix_web::error::Error> {
    let params = actix_web::Path::<TemplateParams>::extract(req).unwrap();
    let ssr = params.ssr;
    let s2 = getStr();
    let s: String = if ssr { s2.to_owned() } else { "".to_owned() };
    let context = TemplateContext {
        parent: "index".to_owned(),
        name: "Roman".to_owned(),
        content: s.to_owned(),
        items: vec!["One", "Two", "Three"]
            .iter()
            .map(|s| s.to_string())
            .collect(),
    };

    let doc: DOMTree<String> = doc(
        html!(

"Hello Kitty"

                    "She is not a cat. She is a human girl."

                { (0..3).map(|_| html!(

                        "Her name is Kitty White."

                )) }

                    "We still don't know how she eats."

"Hello Kitty"

                "DYNAMIC_CONTENT"

        ),
        context,
    );
    let mut doc_str = "<!doctype html>".to_owned() + &doc.to_string();

    // https://users.rust-lang.org/t/how-to-create-a-macro-from-dynamic-content/5079
    doc_str = doc_str.replacen("DYNAMIC_CONTENT", &s2, 1);

    result(Ok(HttpResponse::Ok()
        .content_type("text/html")
        .body(doc_str)))
}

fn doc<T: OutputType + 'static>(
    tree: Box<dyn FlowContent<T>>,
    context: TemplateContext,
) -> DOMTree<T> {
    let TemplateContext { name, items, .. } = context;
    html!(

                <title>"Hello Kitty"</title>
                <meta charset="utf-8" />
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <link rel="icon" href="/favicon.ico?v=1" />
                <link href="/styles/normalize.css" rel="stylesheet" type="text/css" media="all" />
                <link href="/styles/style.css" rel="stylesheet" type="text/css" media="all" />
                <base href="/"></base>
                <meta name=Metadata::Author content="Not Sanrio Co., Ltd" />

{text!("{}", name)}

"Here are your items:"

                   {
                    items.iter().map(|item| html!(

{text!("{}", item)}

                    ))
                   }

                { tree }

"Try going to ""/hello/YourName"

    )
}

use std::fs;
use std::fs::OpenOptions;
use std::io::prelude::*;
extern crate time;

fn getStr() -> String {
    fs::copy(
        "../client/src/elm/Main.elm",
        "../client/src/elm/Main.elm.bak",
    )
    .expect("file not copied");
    let stdout: Vec<u8>;
    {
        let mut main_file = OpenOptions::new()
            .append(true)
            .open("../client/src/elm/Main.elm")
            .expect("file not opened");

        let str1 = format!(
            r#"
            view : Html Msg
            view =
                viewWithModel <|
                    Model [] HomeRoute <|
                        Flags "" {time} ""
            "#,
            time = time::get_time().sec
        );

        main_file
            .write(str1.as_bytes())
            .expect("file content not saved");

        stdout = Command::new("node")
            .current_dir(&path::Path::new("../client"))
            .arg("./node_modules/elm-static-html/index.js")
            .arg("-f")
            .arg("src/elm/Main.elm")
            .output()
            .expect("elm-static-html command failed to start")
            .stdout;
    }
    fs::rename(
        "../client/src/elm/Main.elm.bak",
        "../client/src/elm/Main.elm",
    )
    .expect("file not renamed");

    String::from_utf8_lossy(&*stdout)
        .lines()
        .skip_while(|x| x.find("id=\"content\"").is_none())
        .collect()
}

// #[error(404)]
// fn not_found(req: &Request) -> CORS<Template> {
//     let mut map = std::collections::HashMap::new();
//     map.insert("path", req.uri().as_str());
//     CORS::any(Template::render("error/404", &map))
// }

fn main() {
    dotenv().ok();
    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let sys = actix::System::new("hello-world");

    server::new(|| {
        App::new()
            .middleware(middleware::Logger::default())
            .resource("/", |r| r.f(index))
            .resource("/template/{ssr}", |r| r.method(Method::GET).a(template))
    })
    .bind("127.0.0.1:8080")
    .unwrap()
    .start();

    println!("Starting http server: 127.0.0.1:8080");

    let _ = sys.run();

    // println!("{:?}", env::var("data_server").unwrap());
    // rocket::ignite()
    //     .mount(
    //         "/",
    //         routes![
    //             template,
    //             files::favicon,
    //             files::index,
    //             files::redirect_to_index,
    //             files::js,
    //             files::styles,
    //             files::styles_with_query,
    //             files::images
    //         ],
    //     )
    //     .mount(
    //         "/api",
    //         routes![
    //             api::user_information,
    //             api::data_sets,
    //             api::data_set,
    //             api::data_set_category,
    //             api::data_sets_categories,
    //             api::data_sets_new
    //         ],
    //     )
    //     .catch(errors![not_found])
    //     .launch();
}
<|endoftext|>


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

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Displays the Amazon API Gateway REST APIs in the Region.
async fn show_apis(client: &aws_sdk_apigateway::Client) -> Result<(), aws_sdk_apigateway::Error> {
    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    show_apis(&client).await.unwrap();

    Ok(())
}
<commit_msg>Updated API Gateway code example based on feedback.<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Displays the Amazon API Gateway REST APIs in the Region.
async fn show_apis(client: &Client) -> Result<(), Error> {
    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    show_apis(&client).await
}
<|endoftext|>


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

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Displays the Amazon API Gateway REST APIs in the Region.
async fn show_apis(client: &Client) -> Result<(), Error> {
    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    show_apis(&client).await
}
<commit_msg>Added snippet tags to API Gateway code example in Rust.<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Displays the Amazon API Gateway REST APIs in the Region.
// snippet-start:[apigateway.rust.get_rest_apis]
async fn show_apis(client: &Client) -> Result<(), Error> {
    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}
// snippet-end:[apigateway.rust.get_rest_apis]

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    show_apis(&client).await
}
<|endoftext|>


<commit_before>use crate::errors::MailboxerError;
use crate::models::{Email, Mailbox};
use crate::pagination::Pagination;
use crate::results::*;
use crate::schema::{email, mailbox};
use crate::utils::{ConnectionPool, LoggedResult};
use crate::vacuum::vacuum_old_mailboxes;
use actix_web::{Json, Path, Query, Result, State};
use diesel;
use diesel::{
    prelude::*,
    result::{DatabaseErrorKind::UniqueViolation, Error::DatabaseError},
    QueryDsl,
};
use failure::Error;
use serde_derive::Deserialize;
use std::time::{Duration, SystemTime};

const _PAGE_SIZE: usize = 1000;

#[derive(Deserialize)]
pub struct NewMailbox {
    address: String,
}

pub fn make_inactive(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mb) = mailbox::table
        .filter(mailbox::columns::address.eq(mailbox_address.as_ref()))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        diesel::update(mailbox::table.filter(mailbox::columns::id.eq(mb.id)))
            .set(
                mailbox::columns::last_activity
                    .eq(SystemTime::now() - Duration::from_secs(500 * 24 * 60 * 60)),
            )
            .execute(&conn)
            .log_errors()?;
        Ok(Success)
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}

#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn vacuum(pool: State<ConnectionPool>) -> Result<Success, MailboxerError> {
    vacuum_old_mailboxes(&pool)
        .map_err(|_| MailboxerError::InternalServerError)
        .log_errors()?;
    Ok(Success)
}

pub fn query_mailboxes(
    (connmgr, pagination): (State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Mailbox>, Error> {
    let query = mailbox::table
        .order_by(mailbox::columns::last_activity.desc())
        .offset(pagination.get_offset())
        .limit(pagination.get_page_size());
    Ok(APIResult::multiple(
        query.load::<Mailbox>(&connmgr.get()?)?,
        pagination.into_inner(),
    ))
}

pub fn query_single_mailbox(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<APIResult<Mailbox>, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mb) = mailbox::table
        .filter(mailbox::columns::address.eq(mailbox_address.as_ref()))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        Ok(APIResult::single(mb))
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}

pub fn create_mailbox(
    (new_mailbox, state): (Json<NewMailbox>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    let address = new_mailbox.into_inner().address;

    let conn = state.get().log_errors()?;

    let res = diesel::insert_into(mailbox::table)
        .values(mailbox::dsl::address.eq(&address))
        .execute(&conn)
        .log_errors();
    if let Err(DatabaseError(UniqueViolation, _)) = res {
        return Err(MailboxerError::MailboxAlreadyExists);
    }
    res.log_errors()?;
    Ok(Success)
}

pub fn delete_mailbox(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    diesel::delete(mailbox::table.filter(mailbox::columns::address.eq(mailbox_address.as_ref())))
        .execute(&pool.get()?)
        .map(|_| Success)
        .log_errors()
        .map_err(MailboxerError::from)
}

pub fn query_unread_emails(
    (address, pool, pagination): (Path<String>, State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Email>, MailboxerError> {
    query_emails(address.as_ref(), &pool, pagination.into_inner(), false)
}

pub fn query_all_emails(
    (address, pool, pagination): (Path<String>, State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Email>, MailboxerError> {
    query_emails(address.as_ref(), &pool, pagination.into_inner(), true)
}

pub fn query_emails(
    address: &str,
    pool: &ConnectionPool,
    pagination: Pagination,
    include_read: bool,
) -> Result<APIResult<Email>, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mailbox) = mailbox::table
        .filter(mailbox::columns::address.eq(address))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        let mut query = email::table.into_boxed();

        query = query.filter(email::columns::mailbox_id.eq(mailbox.id));
        if !include_read {
            query = query.filter(email::columns::read.eq(false));
        }
        query = query.order_by(email::columns::timestamp);
        query = query
            .offset(pagination.get_offset())
            .limit(pagination.get_page_size());

        let emails = query.load::<Email>(&conn)?;
        let unread_ids: Vec<_> = emails.iter().filter(|e| !e.read).map(|e| e.id).collect();

        if !unread_ids.is_empty() {
            diesel::update(email::table.filter(email::columns::id.eq_any(&unread_ids)))
                .set(email::columns::read.eq(true))
                .execute(&conn)?;
        }

        Ok(APIResult::multiple(emails, pagination))
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}
<commit_msg>Fix clippy warnings<commit_after>use crate::errors::MailboxerError;
use crate::models::{Email, Mailbox};
use crate::pagination::Pagination;
use crate::results::*;
use crate::schema::{email, mailbox};
use crate::utils::{ConnectionPool, LoggedResult};
use crate::vacuum::vacuum_old_mailboxes;
use actix_web::{Json, Path, Query, Result, State};
use diesel;
use diesel::{
    prelude::*,
    result::{DatabaseErrorKind::UniqueViolation, Error::DatabaseError},
    QueryDsl,
};
use failure::Error;
use serde_derive::Deserialize;
use std::time::{Duration, SystemTime};

const _PAGE_SIZE: usize = 1000;

#[derive(Deserialize)]
pub struct NewMailbox {
    address: String,
}

pub fn make_inactive(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mb) = mailbox::table
        .filter(mailbox::columns::address.eq(mailbox_address.as_ref()))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        diesel::update(mailbox::table.filter(mailbox::columns::id.eq(mb.id)))
            .set(
                mailbox::columns::last_activity
                    .eq(SystemTime::now() - Duration::from_secs(500 * 24 * 60 * 60)),
            )
            .execute(&conn)
            .log_errors()?;
        Ok(Success)
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}

#[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_pass_by_value))]
pub fn vacuum(pool: State<ConnectionPool>) -> Result<Success, MailboxerError> {
    vacuum_old_mailboxes(&pool)
        .map_err(|_| MailboxerError::InternalServerError)
        .log_errors()?;
    Ok(Success)
}

pub fn query_mailboxes(
    (connmgr, pagination): (State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Mailbox>, Error> {
    let query = mailbox::table
        .order_by(mailbox::columns::last_activity.desc())
        .offset(pagination.get_offset())
        .limit(pagination.get_page_size());
    Ok(APIResult::multiple(
        query.load::<Mailbox>(&connmgr.get()?)?,
        pagination.into_inner(),
    ))
}

pub fn query_single_mailbox(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<APIResult<Mailbox>, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mb) = mailbox::table
        .filter(mailbox::columns::address.eq(mailbox_address.as_ref()))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        Ok(APIResult::single(mb))
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}

pub fn create_mailbox(
    (new_mailbox, state): (Json<NewMailbox>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    let address = new_mailbox.into_inner().address;

    let conn = state.get().log_errors()?;

    let res = diesel::insert_into(mailbox::table)
        .values(mailbox::dsl::address.eq(&address))
        .execute(&conn)
        .log_errors();
    if let Err(DatabaseError(UniqueViolation, _)) = res {
        return Err(MailboxerError::MailboxAlreadyExists);
    }
    res.log_errors()?;
    Ok(Success)
}

pub fn delete_mailbox(
    (mailbox_address, pool): (Path<String>, State<ConnectionPool>),
) -> Result<Success, MailboxerError> {
    diesel::delete(mailbox::table.filter(mailbox::columns::address.eq(mailbox_address.as_ref())))
        .execute(&pool.get()?)
        .map(|_| Success)
        .log_errors()
        .map_err(MailboxerError::from)
}

pub fn query_unread_emails(
    (address, pool, pagination): (Path<String>, State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Email>, MailboxerError> {
    query_emails(address.as_ref(), &pool, pagination.into_inner(), false)
}

pub fn query_all_emails(
    (address, pool, pagination): (Path<String>, State<ConnectionPool>, Query<Pagination>),
) -> Result<APIResult<Email>, MailboxerError> {
    query_emails(address.as_ref(), &pool, pagination.into_inner(), true)
}

pub fn query_emails(
    address: &str,
    pool: &ConnectionPool,
    pagination: Pagination,
    include_read: bool,
) -> Result<APIResult<Email>, MailboxerError> {
    let conn = pool.get()?;
    if let Some(mailbox) = mailbox::table
        .filter(mailbox::columns::address.eq(address))
        .first::<Mailbox>(&conn)
        .optional()?
    {
        let mut query = email::table.into_boxed();

        query = query.filter(email::columns::mailbox_id.eq(mailbox.id));
        if !include_read {
            query = query.filter(email::columns::read.eq(false));
        }
        query = query.order_by(email::columns::timestamp);
        query = query
            .offset(pagination.get_offset())
            .limit(pagination.get_page_size());

        let emails = query.load::<Email>(&conn)?;
        let unread_ids: Vec<_> = emails.iter().filter(|e| !e.read).map(|e| e.id).collect();

        if !unread_ids.is_empty() {
            diesel::update(email::table.filter(email::columns::id.eq_any(&unread_ids)))
                .set(email::columns::read.eq(true))
                .execute(&conn)?;
        }

        Ok(APIResult::multiple(emails, pagination))
    } else {
        Err(MailboxerError::MailboxNotFound)
    }
}
<|endoftext|>


<commit_before>//use core::bits::*;
use bit_field::*;
use core::instruction::Instruction;
use core::register::Reg;
use core::ThumbCode;

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_imm_t1(command: u16) -> Instruction {
    Instruction::LDR_imm {
        rt: Reg::from(command.get_bits(0..3) as u8),
        rn: Reg::from(command.get_bits(3..6) as u8),
        imm32: (command.get_bits(6..11) as u32) << 2,
        index: true,
        add: true,
        wback: false,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_imm_t2(command: u16) -> Instruction {
    Instruction::LDR_imm {
        rt: From::from(command.get_bits(8..11) as u8),
        rn: Reg::SP,
        imm32: (command.get_bits(0..8) as u32) << 2,
        index: true,
        add: true,
        wback: false,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_lit_t1(command: u16) -> Instruction {
    Instruction::LDR_lit {
        rt: Reg::from(command.get_bits(8..11) as u8),
        imm32: (command.get_bits(0..8) as u32) << 2,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_reg_t1(command: u16) -> Instruction {
    Instruction::LDR_reg {
        rt: Reg::from(command.get_bits(0..3) as u8),
        rn: Reg::from(command.get_bits(3..6) as u8),
        rm: Reg::from(command.get_bits(6..9) as u8),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_imm_t3(opcode: u32) -> Instruction {
    // ARMv7-M

    Instruction::LDR_imm {
        rt: From::from(opcode.get_bits(12..16) as u8),
        rn: From::from(opcode.get_bits(16..20) as u8),
        imm32: opcode.get_bits(0..12) << 2,
        index: true,
        add: true,
        wback: false,
        thumb32: true
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_imm_t4(opcode: u32) -> Instruction {
    // ARMv7-M
    Instruction::LDR_imm {
        rt: From::from(opcode.get_bits(12..16) as u8),
        rn: From::from(opcode.get_bits(16..20) as u8),
        imm32: opcode.get_bits(0..8),
        index: opcode.get_bit(10),
        add: opcode.get_bit(9),
        wback: opcode.get_bit(8),
        thumb32: true
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_lit_t2(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_reg_t2(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRBT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRD_imm_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRD_lit_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREXB_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREXH_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREX_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRHT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRSBT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRSHT(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}
<commit_msg>bugfix: invaldid shift on imm32 with LDR_imm t3 decoding<commit_after>//use core::bits::*;
use bit_field::*;
use core::instruction::Instruction;
use core::register::Reg;
use core::ThumbCode;

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_imm_t1(command: u16) -> Instruction {
    Instruction::LDR_imm {
        rt: Reg::from(command.get_bits(0..3) as u8),
        rn: Reg::from(command.get_bits(3..6) as u8),
        imm32: (command.get_bits(6..11) as u32) << 2,
        index: true,
        add: true,
        wback: false,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_imm_t2(command: u16) -> Instruction {
    Instruction::LDR_imm {
        rt: From::from(command.get_bits(8..11) as u8),
        rn: Reg::SP,
        imm32: (command.get_bits(0..8) as u32) << 2,
        index: true,
        add: true,
        wback: false,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_lit_t1(command: u16) -> Instruction {
    Instruction::LDR_lit {
        rt: Reg::from(command.get_bits(8..11) as u8),
        imm32: (command.get_bits(0..8) as u32) << 2,
        thumb32: false
    }
}

#[allow(non_snake_case)]
#[inline]
pub fn decode_LDR_reg_t1(command: u16) -> Instruction {
    Instruction::LDR_reg {
        rt: Reg::from(command.get_bits(0..3) as u8),
        rn: Reg::from(command.get_bits(3..6) as u8),
        rm: Reg::from(command.get_bits(6..9) as u8),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_imm_t3(opcode: u32) -> Instruction {
    // ARMv7-M

    Instruction::LDR_imm {
        rt: From::from(opcode.get_bits(12..16) as u8),
        rn: From::from(opcode.get_bits(16..20) as u8),
        imm32: opcode.get_bits(0..12),
        index: true,
        add: true,
        wback: false,
        thumb32: true
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_imm_t4(opcode: u32) -> Instruction {
    // ARMv7-M
    Instruction::LDR_imm {
        rt: From::from(opcode.get_bits(12..16) as u8),
        rn: From::from(opcode.get_bits(16..20) as u8),
        imm32: opcode.get_bits(0..8),
        index: opcode.get_bit(10),
        add: opcode.get_bit(9),
        wback: opcode.get_bit(8),
        thumb32: true
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_lit_t2(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDR_reg_t2(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRBT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRD_imm_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRD_lit_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREXB_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREXH_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDREX_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRHT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRSBT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRSHT(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}

#[allow(non_snake_case)]
pub fn decode_LDRT_t1(opcode: u32) -> Instruction {
    Instruction::UDF {
        imm32: 0,
        opcode: ThumbCode::from(opcode),
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write as _;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::path::MAIN_SEPARATOR;
use std::str;
use tempfile::NamedTempFile;

use protobuf::descriptor::DescriptorProto;
use protobuf::descriptor::EnumDescriptorProto;
use protobuf::descriptor::FileDescriptorProto;
use protobuf::descriptor::FileDescriptorSet;
use protobuf::descriptor::OneofDescriptorProto;
use protobuf::RepeatedField;
use protobuf_test_common::build::copy_tests_v2_v3;
use protobuf_test_common::build::glob_simple;
use std::process::Command;
use std::process::Stdio;

fn to_paths(iter: impl IntoIterator<Item = impl Into<String>>) -> Vec<PathBuf> {
    iter.into_iter()
        .map(|item| item.into().replace(MAIN_SEPARATOR, "/").into())
        .collect()
}

#[derive(Default, Debug)]
struct TestStats {
    passed: u32,
    passed_marked_skip: u32,
    skipped: u32,
    failed: u32,
}

fn normalize_generated_file(contents: &str) -> String {
    let parsed_by = Regex::new("^// \\.proto file is parsed by.*").unwrap();

    let mut r = String::new();
    let mut inside_descriptor_data = false;
    for line in contents.lines() {
        let line = if inside_descriptor_data {
            if line == "\";" {
                inside_descriptor_data = false;
                line.to_owned()
            } else {
                continue;
            }
        } else if line.starts_with("static file_descriptor_proto_data") {
            inside_descriptor_data = true;
            line.to_owned()
        } else {
            parsed_by.replace(line, "").into_owned()
        };

        writeln!(&mut r, "{}", line).unwrap();
    }

    // sanity check
    assert!(!inside_descriptor_data);

    r
}

fn normalize_generated_file_in_place(path: &Path) {
    let contents = fs::read_to_string(path).unwrap();
    let contents = normalize_generated_file(&contents);
    fs::write(path, &contents).unwrap();
}

fn print_diff(dir: &Path, a: &Path, b: &Path) {
    if cfg!(windows) {
        // likely we don't have `diff` command on Windows
        return;
    }

    // sanity check
    let mut a_full = dir.to_path_buf();
    a_full.push(a);
    assert!(a_full.exists());
    let mut b_full = dir.to_path_buf();
    b_full.push(b);
    assert!(b_full.exists());

    // Not using difference crate because it's slow for large files
    let output = Command::new("diff")
        .current_dir(dir)
        .arg("-u")
        .arg(a)
        .arg(b)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .unwrap();

    print!("{}", str::from_utf8(&output.stdout).unwrap());
    print!("{}", str::from_utf8(&output.stderr).unwrap());
}

fn protoc_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut temp_file = NamedTempFile::new().unwrap();
    protoc::Protoc::from_env_path()
        .descriptor_set_out_args()
        .out(temp_file.path())
        .includes(includes)
        .inputs(inputs)
        .write_descriptor_set()
        .unwrap();
    protobuf::parse_from_reader(&mut temp_file).unwrap()
}

// TODO: expose this utility from protobuf-codegen-pure crate.
fn pure_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut codegen = protobuf_codegen_pure::parse_and_typecheck(includes, inputs).unwrap();
    let relative_paths: HashSet<_> = codegen
        .relative_paths
        .iter()
        .map(|path| path.to_string_lossy())
        .collect();
    codegen
        .file_descriptors
        .retain(|fd| relative_paths.contains(fd.get_name()));
    let mut fds = FileDescriptorSet::new();
    fds.file = RepeatedField::from_vec(codegen.file_descriptors);
    fds
}

fn normalize_descriptor_set(fds: &mut FileDescriptorSet) {
    for desc in &mut fds.file {
        normalize_file_descriptor(desc)
    }
}

fn normalize_file_descriptor(desc: &mut FileDescriptorProto) {
    if !desc.has_syntax() {
        desc.set_syntax("proto2".into())
    }
    for desc in &mut *desc.message_type {
        normalize_descriptor(desc)
    }
    for desc in &mut desc.enum_type {
        normalize_enum_descriptor(desc)
    }

    for desc in &mut desc.extension {
        desc.options.mut_or_default();
    }
    desc.options.mut_or_default();

    // TODO: don't clear services.
    desc.service.clear();
}

fn normalize_enum_descriptor(desc: &mut EnumDescriptorProto) {
    desc.options.mut_or_default();

    for value in &mut desc.value {
        value.options.mut_or_default();
    }
}

fn normalize_oneof_descriptor(desc: &mut OneofDescriptorProto) {
    desc.options.mut_or_default();
}

fn normalize_descriptor(desc: &mut DescriptorProto) {
    for desc in &mut desc.nested_type {
        normalize_descriptor(desc);
    }
    for desc in &mut desc.enum_type {
        normalize_enum_descriptor(desc);
    }
    for desc in &mut desc.oneof_decl {
        normalize_oneof_descriptor(desc);
    }

    // TODO: don't clear options.
    desc.options.clear();

    for field in &mut desc.field {
        field.options.mut_or_default();
    }

    for ext in &mut desc.extension {
        ext.options.mut_or_default();
    }

    for ext in &mut desc.extension_range {
        // If ext range end exceeds max field number,
        // the actual upper limit does not matter.
        // protoc is not consistent in behavior thus flush
        // the value to some arbitrary compatible value.
        if ext.has_end() && ext.get_end() >= 0x20000000 {
            ext.set_end(0x20000000);
        }
    }
}

fn pretty_message<M: protobuf::Message>(message: &M) -> String {
    // TODO: pull this handy utility into the protobuf crate.
    struct FormatMessage<'a, M>(&'a M);

    impl<M: protobuf::Message> fmt::Display for FormatMessage<'_, M> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            protobuf::text_format::fmt(self.0, f)
        }
    }

    format!("{:#}", FormatMessage(message))
}

fn descriptor_for_file<'a>(fds: &'a FileDescriptorSet, file_name: &str) -> &'a FileDescriptorProto {
    for file in &fds.file {
        if Path::new(file.get_name()).file_name().unwrap()
            == Path::new(file_name).file_name().unwrap()
        {
            return file;
        }
    }
    panic!(
        "file not found: {}; all files: {}",
        file_name,
        fds.file
            .iter()
            .map(|f| f.get_name())
            .collect::<Vec<_>>()
            .join(", ")
    );
}

fn test_diff_in<F>(root: &str, sources_dir: &str, include: &str, should_skip: F)
where
    F: Fn(&str) -> bool,
{
    let mut stats = TestStats {
        passed: 0,
        passed_marked_skip: 0,
        skipped: 0,
        failed: 0,
    };

    let mut include_root = Path::new(root).to_path_buf();
    include_root.push(include);

    let include_full = format!("{}/{}", root, include);
    let s_full = format!("{}/{}", root, sources_dir);

    let inputs_glob = format!("{}/*.proto", s_full);
    let inputs = to_paths(glob_simple(&inputs_glob));
    assert!(!inputs.is_empty(), "glob is empty: {}", inputs_glob);
    let includes = to_paths(vec![include_full.as_str(), "../proto"]);

    let temp_dir = tempfile::Builder::new()
        .prefix("protobuf-codegen-identical-test")
        .tempdir()
        .unwrap();

    let protoc_dir = format!("{}/protoc", temp_dir.path().to_str().unwrap());
    let pure_dir = format!("{}/pure", temp_dir.path().to_str().unwrap());

    fs::create_dir(&protoc_dir).unwrap();
    fs::create_dir(&pure_dir).unwrap();

    protoc_rust::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&protoc_dir)
        .run()
        .unwrap();

    protobuf_codegen_pure::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&pure_dir)
        .run()
        .unwrap();

    let mut protoc_descriptors = protoc_descriptor_set(&includes, &inputs);
    let mut pure_descriptors = pure_descriptor_set(&includes, &inputs);
    normalize_descriptor_set(&mut protoc_descriptors);
    normalize_descriptor_set(&mut pure_descriptors);

    for input in &inputs {
        let label = input.strip_prefix(root).unwrap().to_str().unwrap();
        let proto_file_name = input.strip_prefix(&include_root).unwrap().to_str().unwrap();
        let proto_name = input.file_name().unwrap().to_str().unwrap();
        let rs_name = protobuf_codegen::proto_name_to_rs(proto_name);
        let protoc_rs = format!("{}/{}", protoc_dir, rs_name);
        let pure_rs = format!("{}/{}", pure_dir, rs_name);

        normalize_generated_file_in_place(Path::new(&protoc_rs));
        normalize_generated_file_in_place(Path::new(&pure_rs));

        let protoc_rs_contents =
            fs::read_to_string(&protoc_rs).expect(&format!("while reading {}", protoc_rs));
        let pure_rs_contents =
            fs::read_to_string(&pure_rs).expect(&format!("while reading {}", pure_rs));

        let protoc_descriptor_for_file = descriptor_for_file(&protoc_descriptors, &proto_file_name);
        let pure_descriptor_for_file = descriptor_for_file(&pure_descriptors, &proto_file_name);

        let skip = should_skip(input.to_str().unwrap());
        if protoc_rs_contents == pure_rs_contents
            && protoc_descriptor_for_file == pure_descriptor_for_file
        {
            if !skip {
                stats.passed += 1;
                println!("{}: PASSED", label);
            } else {
                stats.passed_marked_skip += 1;
                println!("{}: PASSED BUT MARKED SKIP", label);
            }
        } else {
            if skip {
                stats.skipped += 1;
                println!("{} SKIPPED", label);
            } else {
                stats.failed += 1;
                println!("{} FAILED", label);
            }

            print_diff(
                temp_dir.path(),
                Path::new(&protoc_rs).strip_prefix(temp_dir.path()).unwrap(),
                Path::new(&pure_rs).strip_prefix(temp_dir.path()).unwrap(),
            );

            fs::write(
                format!("{}/{}.descriptors", protoc_dir, proto_name),
                pretty_message(protoc_descriptor_for_file),
            )
            .unwrap();
            fs::write(
                format!("{}/{}.descriptors", pure_dir, proto_name),
                pretty_message(pure_descriptor_for_file),
            )
            .unwrap();
            print_diff(
                temp_dir.path(),
                Path::new(&format!("protoc/{}.descriptors", proto_name)),
                Path::new(&format!("pure/{}.descriptors", proto_name)),
            );
        }
    }

    println!("{:?}", stats);
    assert!(
        stats.passed != 0 || sources_dir == "src/google/protobuf",
        "sanity check"
    );
    assert!(stats.failed == 0, "at least one test failed");
}

fn should_skip_with_marker(path: &str) -> bool {
    fs::read_to_string(path)
        .unwrap()
        .contains("@skip-codegen-identical-test")
}

#[test]
fn common_v2() {
    test_diff_in(
        "../protobuf-test",
        "src/common/v2",
        "src/common/v2",
        should_skip_with_marker,
    );
}

#[test]
fn common_v3() {
    let common_v3_root = tempfile::Builder::new()
        .prefix("common-v3")
        .tempdir()
        .unwrap();
    fs::create_dir_all(format!(
        "{}/src/common/v3",
        common_v3_root.path().to_str().unwrap()
    ))
    .unwrap();

    copy_tests_v2_v3(
        "../protobuf-test/src/common/v2",
        &format!("{}/src/common/v3", common_v3_root.path().to_str().unwrap()),
    );

    test_diff_in(
        common_v3_root.path().to_str().unwrap(),
        "src/common/v3",
        "src/common/v3",
        should_skip_with_marker,
    );
}

#[test]
fn v2() {
    test_diff_in(
        "../protobuf-test",
        "src/v2",
        "src/v2",
        should_skip_with_marker,
    );
}

#[test]
fn v3() {
    test_diff_in(
        "../protobuf-test",
        "src/v3",
        "src/v3",
        should_skip_with_marker,
    );
}

#[test]
fn interop() {
    test_diff_in(
        "../protobuf-test",
        "../interop/cxx",
        "../interop/cxx",
        should_skip_with_marker,
    );
}

#[test]
fn google() {
    test_diff_in("../protobuf-test", "src/google/protobuf", "src", |_| true);
}
<commit_msg>Normalize floats in tests<commit_after>use regex::Regex;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write as _;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::path::MAIN_SEPARATOR;
use std::str;
use tempfile::NamedTempFile;

use protobuf::descriptor::field_descriptor_proto;
use protobuf::descriptor::DescriptorProto;
use protobuf::descriptor::EnumDescriptorProto;
use protobuf::descriptor::FieldDescriptorProto;
use protobuf::descriptor::FileDescriptorProto;
use protobuf::descriptor::FileDescriptorSet;
use protobuf::descriptor::OneofDescriptorProto;
use protobuf::text_format::lexer::float::parse_protobuf_float;
use protobuf::RepeatedField;
use protobuf_test_common::build::copy_tests_v2_v3;
use protobuf_test_common::build::glob_simple;
use std::process::Command;
use std::process::Stdio;

fn to_paths(iter: impl IntoIterator<Item = impl Into<String>>) -> Vec<PathBuf> {
    iter.into_iter()
        .map(|item| item.into().replace(MAIN_SEPARATOR, "/").into())
        .collect()
}

#[derive(Default, Debug)]
struct TestStats {
    passed: u32,
    passed_marked_skip: u32,
    skipped: u32,
    failed: u32,
}

fn normalize_generated_file(contents: &str) -> String {
    let parsed_by = Regex::new("^// \\.proto file is parsed by.*").unwrap();

    let mut r = String::new();
    let mut inside_descriptor_data = false;
    for line in contents.lines() {
        let line = if inside_descriptor_data {
            if line == "\";" {
                inside_descriptor_data = false;
                line.to_owned()
            } else {
                continue;
            }
        } else if line.starts_with("static file_descriptor_proto_data") {
            inside_descriptor_data = true;
            line.to_owned()
        } else {
            parsed_by.replace(line, "").into_owned()
        };

        writeln!(&mut r, "{}", line).unwrap();
    }

    // sanity check
    assert!(!inside_descriptor_data);

    r
}

fn normalize_generated_file_in_place(path: &Path) {
    let contents = fs::read_to_string(path).unwrap();
    let contents = normalize_generated_file(&contents);
    fs::write(path, &contents).unwrap();
}

fn print_diff(dir: &Path, a: &Path, b: &Path) {
    if cfg!(windows) {
        // likely we don't have `diff` command on Windows
        return;
    }

    // sanity check
    let mut a_full = dir.to_path_buf();
    a_full.push(a);
    assert!(a_full.exists());
    let mut b_full = dir.to_path_buf();
    b_full.push(b);
    assert!(b_full.exists());

    // Not using difference crate because it's slow for large files
    let output = Command::new("diff")
        .current_dir(dir)
        .arg("-u")
        .arg(a)
        .arg(b)
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .output()
        .unwrap();

    print!("{}", str::from_utf8(&output.stdout).unwrap());
    print!("{}", str::from_utf8(&output.stderr).unwrap());
}

fn protoc_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut temp_file = NamedTempFile::new().unwrap();
    protoc::Protoc::from_env_path()
        .descriptor_set_out_args()
        .out(temp_file.path())
        .includes(includes)
        .inputs(inputs)
        .write_descriptor_set()
        .unwrap();
    protobuf::parse_from_reader(&mut temp_file).unwrap()
}

// TODO: expose this utility from protobuf-codegen-pure crate.
fn pure_descriptor_set(includes: &[PathBuf], inputs: &[PathBuf]) -> FileDescriptorSet {
    let mut codegen = protobuf_codegen_pure::parse_and_typecheck(includes, inputs).unwrap();
    let relative_paths: HashSet<_> = codegen
        .relative_paths
        .iter()
        .map(|path| path.to_string_lossy())
        .collect();
    codegen
        .file_descriptors
        .retain(|fd| relative_paths.contains(fd.get_name()));
    let mut fds = FileDescriptorSet::new();
    fds.file = RepeatedField::from_vec(codegen.file_descriptors);
    fds
}

fn normalize_descriptor_set(fds: &mut FileDescriptorSet) {
    for desc in &mut fds.file {
        normalize_file_descriptor(desc)
    }
}

fn normalize_file_descriptor(desc: &mut FileDescriptorProto) {
    if !desc.has_syntax() {
        desc.set_syntax("proto2".into())
    }
    for desc in &mut *desc.message_type {
        normalize_descriptor(desc)
    }
    for desc in &mut desc.enum_type {
        normalize_enum_descriptor(desc)
    }

    for desc in &mut desc.extension {
        desc.options.mut_or_default();
    }
    desc.options.mut_or_default();

    // TODO: don't clear services.
    desc.service.clear();
}

fn normalize_enum_descriptor(desc: &mut EnumDescriptorProto) {
    desc.options.mut_or_default();

    for value in &mut desc.value {
        value.options.mut_or_default();
    }
}

fn normalize_oneof_descriptor(desc: &mut OneofDescriptorProto) {
    desc.options.mut_or_default();
}

fn normalize_descriptor(desc: &mut DescriptorProto) {
    for desc in &mut desc.nested_type {
        normalize_descriptor(desc);
    }
    for desc in &mut desc.enum_type {
        normalize_enum_descriptor(desc);
    }
    for desc in &mut desc.oneof_decl {
        normalize_oneof_descriptor(desc);
    }

    // TODO: don't clear options.
    desc.options.clear();

    for field in &mut desc.field {
        normalize_field(field);
    }

    for ext in &mut desc.extension {
        ext.options.mut_or_default();
    }

    for ext in &mut desc.extension_range {
        // If ext range end exceeds max field number,
        // the actual upper limit does not matter.
        // protoc is not consistent in behavior thus flush
        // the value to some arbitrary compatible value.
        if ext.has_end() && ext.get_end() >= 0x20000000 {
            ext.set_end(0x20000000);
        }
    }
}

fn normalize_field(field: &mut FieldDescriptorProto) {
    field.options.mut_or_default();

    if field.has_default_value() {
        if field.get_field_type() == field_descriptor_proto::Type::TYPE_FLOAT {
            field.set_default_value(format!(
                "{}",
                parse_protobuf_float(field.get_default_value()).unwrap()
            ));
        }
    }
}

fn pretty_message<M: protobuf::Message>(message: &M) -> String {
    // TODO: pull this handy utility into the protobuf crate.
    struct FormatMessage<'a, M>(&'a M);

    impl<M: protobuf::Message> fmt::Display for FormatMessage<'_, M> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            protobuf::text_format::fmt(self.0, f)
        }
    }

    format!("{:#}", FormatMessage(message))
}

fn descriptor_for_file<'a>(fds: &'a FileDescriptorSet, file_name: &str) -> &'a FileDescriptorProto {
    for file in &fds.file {
        if Path::new(file.get_name()).file_name().unwrap()
            == Path::new(file_name).file_name().unwrap()
        {
            return file;
        }
    }
    panic!(
        "file not found: {}; all files: {}",
        file_name,
        fds.file
            .iter()
            .map(|f| f.get_name())
            .collect::<Vec<_>>()
            .join(", ")
    );
}

fn test_diff_in<F>(root: &str, sources_dir: &str, include: &str, should_skip: F)
where
    F: Fn(&str) -> bool,
{
    let mut stats = TestStats {
        passed: 0,
        passed_marked_skip: 0,
        skipped: 0,
        failed: 0,
    };

    let mut include_root = Path::new(root).to_path_buf();
    include_root.push(include);

    let include_full = format!("{}/{}", root, include);
    let s_full = format!("{}/{}", root, sources_dir);

    let inputs_glob = format!("{}/*.proto", s_full);
    let inputs = to_paths(glob_simple(&inputs_glob));
    assert!(!inputs.is_empty(), "glob is empty: {}", inputs_glob);
    let includes = to_paths(vec![include_full.as_str(), "../proto"]);

    let temp_dir = tempfile::Builder::new()
        .prefix("protobuf-codegen-identical-test")
        .tempdir()
        .unwrap();

    let protoc_dir = format!("{}/protoc", temp_dir.path().to_str().unwrap());
    let pure_dir = format!("{}/pure", temp_dir.path().to_str().unwrap());

    fs::create_dir(&protoc_dir).unwrap();
    fs::create_dir(&pure_dir).unwrap();

    protoc_rust::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&protoc_dir)
        .run()
        .unwrap();

    protobuf_codegen_pure::Codegen::new()
        .inputs(&inputs)
        .includes(&includes)
        .out_dir(&pure_dir)
        .run()
        .unwrap();

    let mut protoc_descriptors = protoc_descriptor_set(&includes, &inputs);
    let mut pure_descriptors = pure_descriptor_set(&includes, &inputs);
    normalize_descriptor_set(&mut protoc_descriptors);
    normalize_descriptor_set(&mut pure_descriptors);

    for input in &inputs {
        let label = input.strip_prefix(root).unwrap().to_str().unwrap();
        let proto_file_name = input.strip_prefix(&include_root).unwrap().to_str().unwrap();
        let proto_name = input.file_name().unwrap().to_str().unwrap();
        let rs_name = protobuf_codegen::proto_name_to_rs(proto_name);
        let protoc_rs = format!("{}/{}", protoc_dir, rs_name);
        let pure_rs = format!("{}/{}", pure_dir, rs_name);

        normalize_generated_file_in_place(Path::new(&protoc_rs));
        normalize_generated_file_in_place(Path::new(&pure_rs));

        let protoc_rs_contents =
            fs::read_to_string(&protoc_rs).expect(&format!("while reading {}", protoc_rs));
        let pure_rs_contents =
            fs::read_to_string(&pure_rs).expect(&format!("while reading {}", pure_rs));

        let protoc_descriptor_for_file = descriptor_for_file(&protoc_descriptors, &proto_file_name);
        let pure_descriptor_for_file = descriptor_for_file(&pure_descriptors, &proto_file_name);

        let skip = should_skip(input.to_str().unwrap());
        if protoc_rs_contents == pure_rs_contents
            && protoc_descriptor_for_file == pure_descriptor_for_file
        {
            if !skip {
                stats.passed += 1;
                println!("{}: PASSED", label);
            } else {
                stats.passed_marked_skip += 1;
                println!("{}: PASSED BUT MARKED SKIP", label);
            }
        } else {
            if skip {
                stats.skipped += 1;
                println!("{} SKIPPED", label);
            } else {
                stats.failed += 1;
                println!("{} FAILED", label);
            }

            print_diff(
                temp_dir.path(),
                Path::new(&protoc_rs).strip_prefix(temp_dir.path()).unwrap(),
                Path::new(&pure_rs).strip_prefix(temp_dir.path()).unwrap(),
            );

            fs::write(
                format!("{}/{}.descriptors", protoc_dir, proto_name),
                pretty_message(protoc_descriptor_for_file),
            )
            .unwrap();
            fs::write(
                format!("{}/{}.descriptors", pure_dir, proto_name),
                pretty_message(pure_descriptor_for_file),
            )
            .unwrap();
            print_diff(
                temp_dir.path(),
                Path::new(&format!("protoc/{}.descriptors", proto_name)),
                Path::new(&format!("pure/{}.descriptors", proto_name)),
            );
        }
    }

    println!("{:?}", stats);
    assert!(
        stats.passed != 0 || sources_dir == "src/google/protobuf",
        "sanity check"
    );
    assert!(stats.failed == 0, "at least one test failed");
}

fn should_skip_with_marker(path: &str) -> bool {
    fs::read_to_string(path)
        .unwrap()
        .contains("@skip-codegen-identical-test")
}

#[test]
fn common_v2() {
    test_diff_in(
        "../protobuf-test",
        "src/common/v2",
        "src/common/v2",
        should_skip_with_marker,
    );
}

#[test]
fn common_v3() {
    let common_v3_root = tempfile::Builder::new()
        .prefix("common-v3")
        .tempdir()
        .unwrap();
    fs::create_dir_all(format!(
        "{}/src/common/v3",
        common_v3_root.path().to_str().unwrap()
    ))
    .unwrap();

    copy_tests_v2_v3(
        "../protobuf-test/src/common/v2",
        &format!("{}/src/common/v3", common_v3_root.path().to_str().unwrap()),
    );

    test_diff_in(
        common_v3_root.path().to_str().unwrap(),
        "src/common/v3",
        "src/common/v3",
        should_skip_with_marker,
    );
}

#[test]
fn v2() {
    test_diff_in(
        "../protobuf-test",
        "src/v2",
        "src/v2",
        should_skip_with_marker,
    );
}

#[test]
fn v3() {
    test_diff_in(
        "../protobuf-test",
        "src/v3",
        "src/v3",
        should_skip_with_marker,
    );
}

#[test]
fn interop() {
    test_diff_in(
        "../protobuf-test",
        "../interop/cxx",
        "../interop/cxx",
        should_skip_with_marker,
    );
}

#[test]
fn google() {
    test_diff_in("../protobuf-test", "src/google/protobuf", "src", |_| true);
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate tina;

use chrono::*;
use tina::*;

struct MiniEEW {
	issue_pattern: IssuePattern,
	kind: Kind,
	status: Status,
	detail: Option<MiniEEWDetail>,
}

struct MiniEEWDetail {
	depth: Option<f32>,
	magnitude: Option<f32>,
	maximum_intensity: Option<IntensityClass>,
	warning_status: WarningStatus,
}

impl From<MiniEEW> for EEW {

	fn from(v: MiniEEW) -> EEW
	{
		EEW {
			issue_pattern: v.issue_pattern, source: Source::Tokyo, kind: v.kind,
			issued_at: UTC.ymd(2010, 1, 1).and_hms(1, 0, 2),
			occurred_at: UTC.ymd(2010, 1, 1).and_hms(0, 55, 59),
			id: "ND20100101005559".to_owned(), status: v.status, number: 10,
			detail: v.detail.map(|d| d.into()),
		}
	}
}

impl From<MiniEEWDetail> for EEWDetail {

	fn from(v: MiniEEWDetail) -> EEWDetail
	{
		EEWDetail {
			epicenter_name: "奈良県".to_owned(), epicenter: (34.4, 135.7),
			depth: v.depth, magnitude: v.magnitude,
			maximum_intensity: v.maximum_intensity,
			epicenter_accuracy: EpicenterAccuracy::GridSearchLow,
			depth_accuracy: DepthAccuracy::GridSearchLow,
			magnitude_accuracy: MagnitudeAccuracy::SWave,
			epicenter_category: EpicenterCategory::Land,
			warning_status: v.warning_status,
			intensity_change: IntensityChange::Unknown,
			change_reason: ChangeReason::Unknown,
			area_info: vec!{},
		}
	}
}

#[test]
fn it_should_format_cancel_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::Cancel,
		kind: Kind::Cancel,
		status: Status::Normal,
		detail: None,
	};

	let expected = "[取消] --- | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_intensity_only_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::IntensityOnly,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: None,
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Forecast,
		}),
	};

	let expected =
		"[速報] 奈良県 震度5弱 M--- 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_low_accuracy_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::LowAccuracy,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Forecast,
		}),
	};

	let expected =
		"[予報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_high_accuracy_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Forecast,
		}),
	};

	let expected =
		"[予報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_intensity_only()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::IntensityOnly,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: None,
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報] 奈良県 震度5弱 M--- 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_low_accuracy()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::LowAccuracy,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_high_accuracy()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_last_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::Last,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報/最終] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_last_with_correction_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::LastWithCorrection,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報/最終] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_correction_eew()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::Correction,
		detail: Some(MiniEEWDetail {
			depth: Some(10.0),
			magnitude: Some(5.9),
			maximum_intensity: Some(IntensityClass::FiveLower),
			warning_status: WarningStatus::Alert,
		}),
	};

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_eew_with_unknown_values()
{
	let eew = MiniEEW {
		issue_pattern: IssuePattern::HighAccuracy,
		kind: Kind::Normal,
		status: Status::Normal,
		detail: Some(MiniEEWDetail {
			depth: None,
			magnitude: None,
			maximum_intensity: None,
			warning_status: WarningStatus::Forecast,
		}),
	};

	let expected =
		"[予報] 奈良県 震度不明 M--- ---km (N34.4/E135.7) 09:55:59発生 | 第10報 ND20100101005559".to_owned();

	let result = ja_format_eew_short(&eew.into(), None);

	assert_eq!(result, Some(expected));
}
<commit_msg>Remove MiniEEW from ja_translator_normal_test.rs<commit_after>extern crate chrono;
extern crate tina;

use chrono::*;
use tina::*;

mod eew_builder;
use eew_builder::*;

struct MiniEEW {
	issue_pattern: IssuePattern,
	kind: Kind,
	status: Status,
	detail: Option<MiniEEWDetail>,
}

struct MiniEEWDetail {
	depth: Option<f32>,
	magnitude: Option<f32>,
	maximum_intensity: Option<IntensityClass>,
	warning_status: WarningStatus,
}

impl From<MiniEEW> for EEW {

	fn from(v: MiniEEW) -> EEW
	{
		EEW {
			issue_pattern: v.issue_pattern, source: Source::Tokyo, kind: v.kind,
			issued_at: UTC.ymd(2010, 1, 1).and_hms(1, 0, 2),
			occurred_at: UTC.ymd(2010, 1, 1).and_hms(0, 55, 59),
			id: "ND20100101005559".to_owned(), status: v.status, number: 10,
			detail: v.detail.map(|d| d.into()),
		}
	}
}

impl From<MiniEEWDetail> for EEWDetail {

	fn from(v: MiniEEWDetail) -> EEWDetail
	{
		EEWDetail {
			epicenter_name: "奈良県".to_owned(), epicenter: (34.4, 135.7),
			depth: v.depth, magnitude: v.magnitude,
			maximum_intensity: v.maximum_intensity,
			epicenter_accuracy: EpicenterAccuracy::GridSearchLow,
			depth_accuracy: DepthAccuracy::GridSearchLow,
			magnitude_accuracy: MagnitudeAccuracy::SWave,
			epicenter_category: EpicenterCategory::Land,
			warning_status: v.warning_status,
			intensity_change: IntensityChange::Unknown,
			change_reason: ChangeReason::Unknown,
			area_info: vec!{},
		}
	}
}

#[test]
fn it_should_format_cancel_eew()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::Cancel)
		.kind(Kind::Cancel)
		.detail_none()
		.build();

	let expected = "[取消] --- | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_intensity_only_eew()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::IntensityOnly)
		.magnitude(None)
		.build();

	let expected =
		"[速報] 奈良県 震度5弱 M--- 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_low_accuracy_eew()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::LowAccuracy)
		.build();

	let expected =
		"[予報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_high_accuracy_eew()
{
	let eew = EEWBuilder::new()
		.build();

	let expected =
		"[予報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_intensity_only()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::IntensityOnly)
		.magnitude(None)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報] 奈良県 震度5弱 M--- 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_low_accuracy()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::LowAccuracy)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_alert_eew_with_high_accuracy()
{
	let eew = EEWBuilder::new()
		.issue_pattern(IssuePattern::HighAccuracy)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_last_eew()
{
	let eew = EEWBuilder::new()
		.status(Status::Last)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報/最終] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_last_with_correction_eew()
{
	let eew = EEWBuilder::new()
		.status(Status::LastWithCorrection)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報/最終] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_correction_eew()
{
	let eew = EEWBuilder::new()
		.status(Status::Correction)
		.warning_status(WarningStatus::Alert)
		.build();

	let expected =
		"[警報] 奈良県 震度5弱 M5.9 10km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}

#[test]
fn it_should_format_eew_with_unknown_values()
{
	let eew = EEWBuilder::new()
		.depth(None)
		.magnitude(None)
		.maximum_intensity(None)
		.build();

	let expected =
		"[予報] 奈良県 震度不明 M--- ---km (N34.4/E135.7) 09:55:59発生 | 第10報 NDXXXX".to_owned();

	let result = ja_format_eew_short(&eew, None);

	assert_eq!(result, Some(expected));
}
<|endoftext|>