<commit_before>use std::process::Command;
use std::os;
use std::fs;
use std::env;
use time;
use std::path::PathBuf;

macro_rules! done {
    ($v:expr) => {{
        println!(" ...OK");
        $v
    }}
}

macro_rules! pretend {
    ($v:expr) => {
        print!(" ([Pretend] {})", $v);
    }
}

pub struct Facteur {
    basedir: String,
    git: Option<String>,
    ts: String,
    pretend: bool
}

impl Facteur {
    pub fn new(dir: &str, pretend: bool) -> Self {
        Facteur {
            basedir: dir.to_string(),
            git: None,
            ts: Self::ts(),
            pretend: pretend
        }
    }

    pub fn git(mut self, git: &str) -> Self {
        self.git = Some(git.to_string());
        self
    }

    pub fn welcome(self, text: &str) -> Self {
        println!("{}", text);
        self
    }

    pub fn canonicalize_basedir(mut self) -> Self {
        self.basedir = Self::canonicalize(&self.basedir);
        self
    }

    pub fn mkdir_base(self) -> Self {
        print!("Making basedir {}", &self.basedir);
        match self.pretend {
            false => {
                Self::mkdir_or_die(&self.basedir);
                Self::mkdir_or_die(&format!("{}/releases", self.basedir));
                Self::mkdir_or_die(&format!("{}/shared", self.basedir));
            },
            true => {
                pretend!(&format!("mkdir {}", self.basedir));
                pretend!(&format!("mkdir {}/releases", self.basedir));
                pretend!(&format!("mkdir {}/shared", self.basedir));
            }
        }
        done!(self)
    }

    pub fn mkdir_release(self) -> Self {
        print!("Initializing Release dir");
        match self.pretend {
            false => Self::mkdir_or_die(&self.release_dir()),
            true => pretend!(format!("mkdir {}", &self.release_dir()))
        }
        done!(self)
    }

    fn release_dir(&self) -> String {
        format!("{}/releases/{}", self.basedir, &self.ts)
    }

    pub fn clone(self) -> Self {
        print!("Checkout");
        {
            let git = self.git.as_ref().unwrap();

            match self.pretend {
                false => {
                    let output = Command::new("git")
                        .arg("clone")
                        .arg(git)
                        .arg(&self.release_dir())
                        .output()
                        .expect("Failed to clone repo");

                    println!("{}", String::from_utf8_lossy(&output.stdout));
                    println!("{}", String::from_utf8_lossy(&output.stderr));

                    if !output.status.success() {
                        Self::abort("Failed to clone repo");
                    }
                },
                true => pretend!(format ! ("git clone {:?} {}", git, &self.release_dir()))
            }
        }
        done!(self)
    }

    pub fn composer(self) -> Self {
        print!("Composer install");
        match self.pretend {
            false => {
                let output = Command::new("composer")
                    .arg("install")
                    .arg("-d")
                    .arg(&self.release_dir())
                    .arg("--no-dev")
                    .arg("--prefer-dist")
                    .output()
                    .expect("Composer installation failed");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Composer installation failed");
                }
            },
            true => {
                pretend!(format!("composer install -d {} --no-dev --prefer-dist", &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_env(self) -> Self {
        print!("Init .env file");
        match self.pretend {
            false => {
                fs::copy(
                    format!("{}/.env.example", &self.release_dir()),
                    format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/.env.example {}/.env", &self.release_dir(), &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_storage(self) -> Self {
        print!("Init storage");
        match self.pretend {
            false => {
                fs::rename(
                    format!("{}/storage", &self.release_dir()),
                    format!("{}/shared/storage", &self.basedir)).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                });
            },
            true => {
                pretend!(format!("mv {}/storage {}/shared/storage", &self.release_dir(), &self.basedir));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn symlink(self) -> Self {
        print!("Symlink");
        match self.pretend {
            false => {
                fs::remove_file(format!("{}/current", &self.basedir)).ok();
                os::unix::fs::symlink(&self.release_dir(), format!("{}/current", &self.basedir))
                    .unwrap_or_else(|why| { Self::abort(&format!("Cannot create symlink. {:?}", why.kind())); });
            },
            true => {
                pretend!(format!("rm -Rf {}/current", &self.basedir));
                pretend!(format!("ln -s {} {}/current", &self.release_dir(), &self.basedir));
            }
        }
        done!(self)
    }

    pub fn bye(self, text: &str) -> Self {
        println!("{}", text);
        done!(self)
    }
    pub fn copy_env(self) -> Self {
        print!("copy_env");
        match self.pretend {
            false => {
                fs::copy(format!("{}/current/.env", &self.basedir), format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/current/.env {}/.env", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn switch_storage(self) -> Self {
        print!("Link storage dir");
        match self.pretend {
            false => {
                fs::remove_dir_all(format!("{}/storage", &self.release_dir())).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                }
                );
            },
            true => {
                pretend!(format!("rm {}/storage", &self.release_dir()));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn migrate(self) -> Self {
        print!("Artisan migrate");
        match self.pretend {
            false => {
                let output = Command::new("php")
                    .current_dir(&self.release_dir())
                    .arg("artisan")
                    .arg("migrate")
                    .arg("--force")
                    .output()
                    .expect("Failed to migrate");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Artisan migration failed");
                }
            },
            true => {
                pretend!(format!("php artisan migrate --force"));
            }
        }
        done!(self)
    }
    pub fn clean_old_releases(self) -> Self {
        print!("Delete old releases");
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", &self.basedir));
        for _ in 0..3 {
            paths.pop();
        }
        for path in paths {
            println!("Destroying old release: {}", path.path().display());
            match self.pretend {
                false => {
                    fs::remove_dir_all(path.path()).ok();
                },
                true => {
                    pretend!(format!("rm -Rf {}", path.path().display()))
                }
            }
        }
        done!(self)
    }

    pub fn rollback(self) -> Self {
        print!("Rollback");
        let previous = &Self::get_previous_release(&self.basedir);
        fs::remove_file(format!("{}/current", &self.basedir)).ok();
        os::unix::fs::symlink(previous, format!("{}/current", &self.basedir))
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
            });
        done!(self)
    }

    fn canonicalize(dir: &str) -> String {
        match fs::canonicalize(&PathBuf::from(&dir)) {
            Ok(dir) => dir.as_os_str().to_str().unwrap().to_string(),
            Err(_) => format!("{}/{}", env::current_dir().unwrap().display(), dir)
        }
    }

    fn mkdir_or_die(dir: &str) {
        fs::create_dir_all(dir)
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create dir. {:?}", why.kind()));
            });
    }

    fn abort(msg: &str) -> ! {
        panic!("ABORTED: {}", msg)
    }

    fn ts() -> String {
        time::now().strftime("%Y%m%d%H%M%S").unwrap().to_string()
    }

    fn get_sorted_paths(dir: &str) -> Vec<fs::DirEntry> {
        let mut paths: Vec<_> = fs::read_dir(dir).unwrap()
            .map(|r| r.unwrap())
            .collect();
        paths.sort_by_key(|dir| dir.path());
        paths
    }

    fn get_previous_release(dir: &str) -> String {
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", dir));
        paths.pop();
        format!("{}", paths.pop().unwrap().path().display())
    }
}
<commit_msg>Update facteur.rs<commit_after>use std::process::Command;
use std::os;
use std::fs;
use std::env;
use time;
use std::path::PathBuf;

macro_rules! done {
    ($v:expr) => {{
        println!(" ...OK");
        $v
    }}
}

macro_rules! pretend {
    ($v:expr) => {
        print!(" ([Pretend] {})", $v);
    }
}

pub struct Facteur {
    basedir: String,
    git: Option<String>,
    ts: String,
    pretend: bool
}

impl Facteur {
    pub fn new(dir: &str, pretend: bool) -> Self {
        Facteur {
            basedir: dir.to_string(),
            git: None,
            ts: Self::ts(),
            pretend: pretend
        }
    }

    pub fn git(mut self, git: &str) -> Self {
        self.git = Some(git.to_string());
        self
    }

    pub fn welcome(self, text: &str) -> Self {
        println!("{}", text);
        self
    }

    pub fn canonicalize_basedir(mut self) -> Self {
        self.basedir = Self::canonicalize(&self.basedir);
        self
    }

    pub fn mkdir_base(self) -> Self {
        print!("Making basedir {}", &self.basedir);
        match self.pretend {
            false => {
                Self::mkdir_or_die(&self.basedir);
                Self::mkdir_or_die(&format!("{}/releases", self.basedir));
                Self::mkdir_or_die(&format!("{}/shared", self.basedir));
            },
            true => {
                pretend!(&format!("mkdir {}", self.basedir));
                pretend!(&format!("mkdir {}/releases", self.basedir));
                pretend!(&format!("mkdir {}/shared", self.basedir));
            }
        }
        done!(self)
    }

    pub fn mkdir_release(self) -> Self {
        print!("Initializing Release dir");
        match self.pretend {
            false => Self::mkdir_or_die(&self.release_dir()),
            true => pretend!(format!("mkdir {}", &self.release_dir()))
        }
        done!(self)
    }

    fn release_dir(&self) -> String {
        format!("{}/releases/{}", self.basedir, &self.ts)
    }

    pub fn clone(self) -> Self {
        print!("Checkout");
        {
            let git = self.git.as_ref().unwrap();

            match self.pretend {
                false => {
                    let output = Command::new("git")
                        .arg("clone")
                        .arg(git)
                        .arg(&self.release_dir())
                        .output()
                        .expect("Failed to clone repo");

                    println!("{}", String::from_utf8_lossy(&output.stdout));
                    println!("{}", String::from_utf8_lossy(&output.stderr));

                    if !output.status.success() {
                        Self::abort("Failed to clone repo");
                    }
                },
                true => pretend!(format ! ("git clone {:?} {}", git, &self.release_dir()))
            }
        }
        done!(self)
    }

    pub fn composer(self) -> Self {
        print!("Composer install");
        match self.pretend {
            false => {
                let output = Command::new("composer")
                    .arg("install")
                    .arg("-d")
                    .arg(&self.release_dir())
                    .arg("--no-dev")
                    .arg("--prefer-dist")
                    .arg("--optimize-autoloader")
                    .output()
                    .expect("Composer installation failed");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Composer installation failed");
                }
            },
            true => {
                pretend!(format!("composer install -d {} --no-dev --prefer-dist", &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_env(self) -> Self {
        print!("Init .env file");
        match self.pretend {
            false => {
                fs::copy(
                    format!("{}/.env.example", &self.release_dir()),
                    format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/.env.example {}/.env", &self.release_dir(), &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_storage(self) -> Self {
        print!("Init storage");
        match self.pretend {
            false => {
                fs::rename(
                    format!("{}/storage", &self.release_dir()),
                    format!("{}/shared/storage", &self.basedir)).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                });
            },
            true => {
                pretend!(format!("mv {}/storage {}/shared/storage", &self.release_dir(), &self.basedir));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn symlink(self) -> Self {
        print!("Symlink");
        match self.pretend {
            false => {
                fs::remove_file(format!("{}/current", &self.basedir)).ok();
                os::unix::fs::symlink(&self.release_dir(), format!("{}/current", &self.basedir))
                    .unwrap_or_else(|why| { Self::abort(&format!("Cannot create symlink. {:?}", why.kind())); });
            },
            true => {
                pretend!(format!("rm -Rf {}/current", &self.basedir));
                pretend!(format!("ln -s {} {}/current", &self.release_dir(), &self.basedir));
            }
        }
        done!(self)
    }

    pub fn bye(self, text: &str) -> Self {
        println!("{}", text);
        done!(self)
    }
    pub fn copy_env(self) -> Self {
        print!("copy_env");
        match self.pretend {
            false => {
                fs::copy(format!("{}/current/.env", &self.basedir), format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/current/.env {}/.env", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn switch_storage(self) -> Self {
        print!("Link storage dir");
        match self.pretend {
            false => {
                fs::remove_dir_all(format!("{}/storage", &self.release_dir())).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                }
                );
            },
            true => {
                pretend!(format!("rm {}/storage", &self.release_dir()));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn migrate(self) -> Self {
        print!("Artisan migrate");
        match self.pretend {
            false => {
                let output = Command::new("php")
                    .current_dir(&self.release_dir())
                    .arg("artisan")
                    .arg("migrate")
                    .arg("--force")
                    .output()
                    .expect("Failed to migrate");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Artisan migration failed");
                }
            },
            true => {
                pretend!(format!("php artisan migrate --force"));
            }
        }
        done!(self)
    }
    pub fn clean_old_releases(self) -> Self {
        print!("Delete old releases");
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", &self.basedir));
        for _ in 0..3 {
            paths.pop();
        }
        for path in paths {
            println!("Destroying old release: {}", path.path().display());
            match self.pretend {
                false => {
                    fs::remove_dir_all(path.path()).ok();
                },
                true => {
                    pretend!(format!("rm -Rf {}", path.path().display()))
                }
            }
        }
        done!(self)
    }

    pub fn rollback(self) -> Self {
        print!("Rollback");
        let previous = &Self::get_previous_release(&self.basedir);
        fs::remove_file(format!("{}/current", &self.basedir)).ok();
        os::unix::fs::symlink(previous, format!("{}/current", &self.basedir))
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
            });
        done!(self)
    }

    fn canonicalize(dir: &str) -> String {
        match fs::canonicalize(&PathBuf::from(&dir)) {
            Ok(dir) => dir.as_os_str().to_str().unwrap().to_string(),
            Err(_) => format!("{}/{}", env::current_dir().unwrap().display(), dir)
        }
    }

    fn mkdir_or_die(dir: &str) {
        fs::create_dir_all(dir)
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create dir. {:?}", why.kind()));
            });
    }

    fn abort(msg: &str) -> ! {
        panic!("ABORTED: {}", msg)
    }

    fn ts() -> String {
        time::now().strftime("%Y%m%d%H%M%S").unwrap().to_string()
    }

    fn get_sorted_paths(dir: &str) -> Vec<fs::DirEntry> {
        let mut paths: Vec<_> = fs::read_dir(dir).unwrap()
            .map(|r| r.unwrap())
            .collect();
        paths.sort_by_key(|dir| dir.path());
        paths
    }

    fn get_previous_release(dir: &str) -> String {
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", dir));
        paths.pop();
        format!("{}", paths.pop().unwrap().path().display())
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc, Mutex,
};

use futures::{future::FutureExt, select_biased};
use tbot::{
    types::parameters::{self, WebPagePreviewState},
    Bot,
};
use tokio::{
    self,
    stream::StreamExt,
    sync::Notify,
    time::{self, delay_for, delay_queue::DelayQueue, Duration, Instant},
};

use crate::client::pull_feed;
use crate::data::{Database, Feed, FeedUpdate};
use crate::messages::{format_large_msg, Escape};

pub fn start(bot: Bot, db: Arc<Mutex<Database>>, min_interval: u32, max_interval: u32) {
    let mut queue = FetchQueue::new();
    // TODO: Don't use interval, it can accumulate ticks
    // replace it with delay_until
    let mut interval = time::interval_at(Instant::now(), Duration::from_secs(min_interval as u64));
    let throttle = Throttle::new(min_interval as usize);
    tokio::spawn(async move {
        loop {
            select_biased! {
                feed = queue.next().fuse() => {
                    let feed = feed.expect("unreachable");
                    let bot = bot.clone();
                    let db = db.clone();
                    let opportunity = throttle.acquire();
                    tokio::spawn(async move {
                        opportunity.wait().await;
                        if let Err(e) = fetch_and_push_updates(bot, db, feed).await {
                            crate::print_error(e);
                        }
                    });
                }
                _ = interval.tick().fuse() => {
                    let feeds = db.lock().unwrap().all_feeds();
                    for feed in feeds {
                        let feed_interval = cmp::min(
                            feed.ttl.map(|ttl| ttl * 60).unwrap_or(min_interval),
                            max_interval,
                        ) as u64 - 1; // after -1, we can stagger with `interval`
                        queue.enqueue(feed, Duration::from_secs(feed_interval));
                    }
                }
            }
        }
    });
}

async fn fetch_and_push_updates(
    bot: Bot,
    db: Arc<Mutex<Database>>,
    feed: Feed,
) -> Result<(), tbot::errors::MethodCall> {
    let new_feed = match pull_feed(&feed.link).await {
        Ok(feed) => feed,
        Err(e) => {
            let down_time = db.lock().unwrap().get_or_update_down_time(&feed.link);
            if down_time.is_none() {
                // user unsubscribed while fetching the feed
                return Ok(());
            }
            // 5 days
            if down_time.unwrap().as_secs() > 5 * 24 * 60 * 60 {
                db.lock().unwrap().reset_down_time(&feed.link);
                let msg = format!(
                    "《{}》\
                     已经连续 5 天拉取出错 ({}),\
                     可能已经关闭, 请取消订阅",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&e.to_user_friendly())
                );
                push_updates(&bot, &db, feed.subscribers, parameters::Text::html(&msg)).await?;
            }
            return Ok(());
        }
    };

    let updates = db.lock().unwrap().update(&feed.link, new_feed);
    for update in updates {
        match update {
            FeedUpdate::Items(items) => {
                let msgs =
                    format_large_msg(format!("{}", Escape(&feed.title)), &items, |item| {
                        let title = item
                            .title
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.title);
                        let link = item
                            .link
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.link);
                        format!("{}", Escape(link), Escape(title))
                    });
                for msg in msgs {
                    push_updates(
                        &bot,
                        &db,
                        feed.subscribers.iter().copied(),
                        parameters::Text::html(&msg),
                    )
                    .await?;
                }
            }
            FeedUpdate::Title(new_title) => {
                let msg = format!(
                    "{} 已更名为 {}",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&new_title)
                );
                push_updates(
                    &bot,
                    &db,
                    feed.subscribers.iter().copied(),
                    parameters::Text::html(&msg),
                )
                .await?;
            }
        }
    }
    Ok(())
}

async fn push_updates<I: IntoIterator<Item = i64>>(
    bot: &Bot,
    db: &Arc<Mutex<Database>>,
    subscribers: I,
    msg: parameters::Text<'_>,
) -> Result<(), tbot::errors::MethodCall> {
    use tbot::errors::MethodCall;
    for mut subscriber in subscribers {
        'retry: for _ in 0..3 {
            match bot
                .send_message(tbot::types::chat::Id(subscriber), msg)
                .web_page_preview(WebPagePreviewState::Disabled)
                .call()
                .await
            {
                Err(MethodCall::RequestError { description, .. })
                    if chat_is_unavailable(&description) =>
                {
                    db.lock().unwrap().delete_subscriber(subscriber);
                }
                Err(MethodCall::RequestError {
                    migrate_to_chat_id: Some(new_chat_id),
                    ..
                }) => {
                    db.lock()
                        .unwrap()
                        .update_subscriber(subscriber, new_chat_id.0);
                    subscriber = new_chat_id.0;
                    continue 'retry;
                }
                Err(MethodCall::RequestError {
                    retry_after: Some(delay),
                    ..
                }) => {
                    time::delay_for(Duration::from_secs(delay)).await;
                    continue 'retry;
                }
                other => {
                    other?;
                }
            }
            break 'retry;
        }
    }
    Ok(())
}

pub fn chat_is_unavailable(s: &str) -> bool {
    s.contains("Forbidden")
        || s.contains("chat not found")
        || s.contains("have no rights")
        || s.contains("need administrator rights")
}

#[derive(Default)]
struct FetchQueue {
    feeds: HashMap<String, Feed>,
    notifies: DelayQueue<String>,
    wakeup: Notify,
}

impl FetchQueue {
    fn new() -> Self {
        Self::default()
    }

    fn enqueue(&mut self, feed: Feed, delay: Duration) -> bool {
        let exists = self.feeds.contains_key(&feed.link);
        if !exists {
            self.notifies.insert(feed.link.clone(), delay);
            self.feeds.insert(feed.link.clone(), feed);
            self.wakeup.notify();
        }
        !exists
    }

    async fn next(&mut self) -> Result<Feed, time::Error> {
        loop {
            if let Some(feed_id) = self.notifies.next().await {
                let feed = self.feeds.remove(feed_id?.get_ref()).unwrap();
                break Ok(feed);
            } else {
                self.wakeup.notified().await;
            }
        }
    }
}

struct Throttle {
    pieces: usize,
    counter: Arc<AtomicUsize>,
}

impl Throttle {
    fn new(pieces: usize) -> Self {
        Throttle {
            pieces,
            counter: Arc::new(AtomicUsize::new(0)),
        }
    }

    fn acquire(&self) -> Opportunity {
        Opportunity {
            n: self.counter.fetch_add(1, Ordering::AcqRel) % self.pieces,
            counter: self.counter.clone(),
        }
    }
}

#[must_use = "Don't lose your opportunity"]
struct Opportunity {
    n: usize,
    counter: Arc<AtomicUsize>,
}

impl Opportunity {
    async fn wait(&self) {
        delay_for(Duration::from_secs(self.n as u64)).await
    }
}

impl Drop for Opportunity {
    fn drop(&mut self) {
        self.counter.fetch_sub(1, Ordering::SeqCst);
    }
}
<commit_msg>fix min_interval<commit_after>use std::cmp;
use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc, Mutex,
};

use futures::{future::FutureExt, select_biased};
use tbot::{
    types::parameters::{self, WebPagePreviewState},
    Bot,
};
use tokio::{
    self,
    stream::StreamExt,
    sync::Notify,
    time::{self, delay_for, delay_queue::DelayQueue, Duration, Instant},
};

use crate::client::pull_feed;
use crate::data::{Database, Feed, FeedUpdate};
use crate::messages::{format_large_msg, Escape};

pub fn start(bot: Bot, db: Arc<Mutex<Database>>, min_interval: u32, max_interval: u32) {
    let mut queue = FetchQueue::new();
    // TODO: Don't use interval, it can accumulate ticks
    // replace it with delay_until
    let mut interval = time::interval_at(Instant::now(), Duration::from_secs(min_interval as u64));
    let throttle = Throttle::new(min_interval as usize);
    tokio::spawn(async move {
        loop {
            select_biased! {
                feed = queue.next().fuse() => {
                    let feed = feed.expect("unreachable");
                    let bot = bot.clone();
                    let db = db.clone();
                    let opportunity = throttle.acquire();
                    tokio::spawn(async move {
                        opportunity.wait().await;
                        if let Err(e) = fetch_and_push_updates(bot, db, feed).await {
                            crate::print_error(e);
                        }
                    });
                }
                _ = interval.tick().fuse() => {
                    let feeds = db.lock().unwrap().all_feeds();
                    for feed in feeds {
                        let feed_interval = cmp::min(
                            cmp::max(feed.ttl.map(|ttl| ttl * 60).unwrap_or(min_interval), min_interval),
                            max_interval,
                        ) as u64 - 1; // after -1, we can stagger with `interval`
                        queue.enqueue(feed, Duration::from_secs(feed_interval));
                    }
                }
            }
        }
    });
}

async fn fetch_and_push_updates(
    bot: Bot,
    db: Arc<Mutex<Database>>,
    feed: Feed,
) -> Result<(), tbot::errors::MethodCall> {
    let new_feed = match pull_feed(&feed.link).await {
        Ok(feed) => feed,
        Err(e) => {
            let down_time = db.lock().unwrap().get_or_update_down_time(&feed.link);
            if down_time.is_none() {
                // user unsubscribed while fetching the feed
                return Ok(());
            }
            // 5 days
            if down_time.unwrap().as_secs() > 5 * 24 * 60 * 60 {
                db.lock().unwrap().reset_down_time(&feed.link);
                let msg = format!(
                    "《{}》\
                     已经连续 5 天拉取出错 ({}),\
                     可能已经关闭, 请取消订阅",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&e.to_user_friendly())
                );
                push_updates(&bot, &db, feed.subscribers, parameters::Text::html(&msg)).await?;
            }
            return Ok(());
        }
    };

    let updates = db.lock().unwrap().update(&feed.link, new_feed);
    for update in updates {
        match update {
            FeedUpdate::Items(items) => {
                let msgs =
                    format_large_msg(format!("{}", Escape(&feed.title)), &items, |item| {
                        let title = item
                            .title
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.title);
                        let link = item
                            .link
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.link);
                        format!("{}", Escape(link), Escape(title))
                    });
                for msg in msgs {
                    push_updates(
                        &bot,
                        &db,
                        feed.subscribers.iter().copied(),
                        parameters::Text::html(&msg),
                    )
                    .await?;
                }
            }
            FeedUpdate::Title(new_title) => {
                let msg = format!(
                    "{} 已更名为 {}",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&new_title)
                );
                push_updates(
                    &bot,
                    &db,
                    feed.subscribers.iter().copied(),
                    parameters::Text::html(&msg),
                )
                .await?;
            }
        }
    }
    Ok(())
}

async fn push_updates<I: IntoIterator<Item = i64>>(
    bot: &Bot,
    db: &Arc<Mutex<Database>>,
    subscribers: I,
    msg: parameters::Text<'_>,
) -> Result<(), tbot::errors::MethodCall> {
    use tbot::errors::MethodCall;
    for mut subscriber in subscribers {
        'retry: for _ in 0..3 {
            match bot
                .send_message(tbot::types::chat::Id(subscriber), msg)
                .web_page_preview(WebPagePreviewState::Disabled)
                .call()
                .await
            {
                Err(MethodCall::RequestError { description, .. })
                    if chat_is_unavailable(&description) =>
                {
                    db.lock().unwrap().delete_subscriber(subscriber);
                }
                Err(MethodCall::RequestError {
                    migrate_to_chat_id: Some(new_chat_id),
                    ..
                }) => {
                    db.lock()
                        .unwrap()
                        .update_subscriber(subscriber, new_chat_id.0);
                    subscriber = new_chat_id.0;
                    continue 'retry;
                }
                Err(MethodCall::RequestError {
                    retry_after: Some(delay),
                    ..
                }) => {
                    time::delay_for(Duration::from_secs(delay)).await;
                    continue 'retry;
                }
                other => {
                    other?;
                }
            }
            break 'retry;
        }
    }
    Ok(())
}

pub fn chat_is_unavailable(s: &str) -> bool {
    s.contains("Forbidden")
        || s.contains("chat not found")
        || s.contains("have no rights")
        || s.contains("need administrator rights")
}

#[derive(Default)]
struct FetchQueue {
    feeds: HashMap<String, Feed>,
    notifies: DelayQueue<String>,
    wakeup: Notify,
}

impl FetchQueue {
    fn new() -> Self {
        Self::default()
    }

    fn enqueue(&mut self, feed: Feed, delay: Duration) -> bool {
        let exists = self.feeds.contains_key(&feed.link);
        if !exists {
            self.notifies.insert(feed.link.clone(), delay);
            self.feeds.insert(feed.link.clone(), feed);
            self.wakeup.notify();
        }
        !exists
    }

    async fn next(&mut self) -> Result<Feed, time::Error> {
        loop {
            if let Some(feed_id) = self.notifies.next().await {
                let feed = self.feeds.remove(feed_id?.get_ref()).unwrap();
                break Ok(feed);
            } else {
                self.wakeup.notified().await;
            }
        }
    }
}

struct Throttle {
    pieces: usize,
    counter: Arc<AtomicUsize>,
}

impl Throttle {
    fn new(pieces: usize) -> Self {
        Throttle {
            pieces,
            counter: Arc::new(AtomicUsize::new(0)),
        }
    }

    fn acquire(&self) -> Opportunity {
        Opportunity {
            n: self.counter.fetch_add(1, Ordering::AcqRel) % self.pieces,
            counter: self.counter.clone(),
        }
    }
}

#[must_use = "Don't lose your opportunity"]
struct Opportunity {
    n: usize,
    counter: Arc<AtomicUsize>,
}

impl Opportunity {
    async fn wait(&self) {
        delay_for(Duration::from_secs(self.n as u64)).await
    }
}

impl Drop for Opportunity {
    fn drop(&mut self) {
        self.counter.fetch_sub(1, Ordering::SeqCst);
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::fmt;
use std::path::Path;

use git;

#[derive(Debug, Clone)]
pub struct Entry<'a> { name: &'a str, time: &'a str, comment: &'a str, commit: &'a str, }

impl<'a> fmt::Display for Entry<'a> {
    fn fmt(& self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Entry: {} {} {}: {}", self.commit, self.time, self.name, self.comment)
    }
}

impl<'a> Entry<'a> {
    pub fn new(name: &'a str, time: &'a str, comment: &'a str, commit: &'a str) -> Entry<'a> {
        Entry { name:name, time: time, comment: comment, commit : commit }
    }

    pub fn name(&self) -> &str { self.name }
    pub fn time(&self) -> &str { self.time }
    pub fn comment(&self) -> &str { self.comment }
    pub fn commit(&self) -> &str { self.commit }
}

pub struct History<'a> {
    cwd: &'a Path,
}

const GIT_ONE_LINE_DETAILS : &'static str = "log --pretty=format:'%h|%an|%cr|%s'";

impl<'a> History<'a> {

    pub fn new(cwd: &Path) -> History { History{ cwd : cwd } }

    pub fn len(&self) -> i32 {
        let mut args: Vec<&str> = vec![GIT_ONE_LINE_DETAILS];
        args.push(" | wc -l");

        git::exec_git(self.cwd, &args[ .. ]);

        -1
    }
}
<commit_msg>Make history use commands as array of strings<commit_after>use std::convert::From;
use std::fmt;
use std::path::Path;

use git;

#[derive(Debug, Clone)]
pub struct Entry<'a> { name: &'a str, time: &'a str, comment: &'a str, commit: &'a str, }

impl<'a> fmt::Display for Entry<'a> {
    fn fmt(& self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Entry: {} {} {}: {}", self.commit, self.time, self.name, self.comment)
    }
}

impl<'a> Entry<'a> {
    pub fn new(name: &'a str, time: &'a str, comment: &'a str, commit: &'a str) -> Entry<'a> {
        Entry { name:name, time: time, comment: comment, commit : commit }
    }

    pub fn name(&self) -> &str { self.name }
    pub fn time(&self) -> &str { self.time }
    pub fn comment(&self) -> &str { self.comment }
    pub fn commit(&self) -> &str { self.commit }
}

pub struct History<'a> {
    cwd: &'a Path,
}

const GIT_ONE_LINE_DETAILS : [&'static str;2] = ["log", "--pretty=format:'%h|%an|%cr|%s'"];

impl<'a> History<'a> {

    pub fn new(cwd: &Path) -> History { History{ cwd : cwd } }

    pub fn len(&self) -> i32 {
        let mut args: Vec<&str> = GIT_ONE_LINE_DETAILS.to_vec();
        args.push(" | wc -l");

        git::exec_git(self.cwd, &args[ .. ]);

        -1
    }
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("proto", self.proto)
            .field("value", &"...")
            .finish()
    }
}

impl Copy for EnumValueDescriptor {}

impl EnumValueDescriptor {
    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto.get_number()
    }

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        self.get_descriptor.descriptor()
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            .field("full_name", &self.full_name)
            .field("..", &"..")
            .finish()
    }
}

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

impl EnumDescriptor {
    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Separate function to reduce generated code size bloat.
    fn make_indices(proto: &EnumDescriptorProto) -> (HashMap<String, usize>, HashMap<i32, usize>) {
        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        (index_by_name, index_by_number)
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let (index_by_name, index_by_number) = EnumDescriptor::make_indices(proto);

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

    /// Find enum variant by name
    pub fn value_by_name<'a>(&'a self, name: &str) -> Option<&'a EnumValueDescriptor> {
        let &index = self.index_by_name.get(name)?;
        Some(&self.values[index])
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<&EnumValueDescriptor> {
        let &index = self.index_by_number.get(&number)?;
        Some(&self.values[index])
    }

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::ProtobufEnum;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// let descriptor: &EnumDescriptor = Label::enum_descriptor_static();
    ///
    /// assert!(descriptor.is::<Label>())
    /// ```
    pub fn is<E: ProtobufEnum>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

#[cfg(rustc_nightly)]
mod cast_impl {
    use super::*;

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<commit_msg>Hide EnumDescriptor::cast from public API<commit_after>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("proto", self.proto)
            .field("value", &"...")
            .finish()
    }
}

impl Copy for EnumValueDescriptor {}

impl EnumValueDescriptor {
    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto.get_number()
    }

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        self.get_descriptor.descriptor()
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            .field("full_name", &self.full_name)
            .field("..", &"..")
            .finish()
    }
}

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

impl EnumDescriptor {
    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &'static str {
        self.proto.get_name()
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Separate function to reduce generated code size bloat.
    fn make_indices(proto: &EnumDescriptorProto) -> (HashMap<String, usize>, HashMap<i32, usize>) {
        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        (index_by_name, index_by_number)
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let (index_by_name, index_by_number) = EnumDescriptor::make_indices(proto);

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

    /// Find enum variant by name
    pub fn value_by_name<'a>(&'a self, name: &str) -> Option<&'a EnumValueDescriptor> {
        let &index = self.index_by_name.get(name)?;
        Some(&self.values[index])
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<&EnumValueDescriptor> {
        let &index = self.index_by_number.get(&number)?;
        Some(&self.values[index])
    }

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::ProtobufEnum;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// let descriptor: &EnumDescriptor = Label::enum_descriptor_static();
    ///
    /// assert!(descriptor.is::<Label>())
    /// ```
    pub fn is<E: ProtobufEnum>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub(crate) fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

#[cfg(rustc_nightly)]
mod cast_impl {
    use super::*;

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<|endoftext|>


<commit_before>/// Utilities to work with Jansson JSON values, which are exposed in the Janus plugin API.
extern crate libc;

use jansson_sys;
use std::error::Error;
use std::fmt;
use std::ffi::{CStr, CString};
use std::mem;
use std::ops::Deref;
use std::slice;
use std::str;
use utils::LibcString;

/// A pointer to a raw Jansson value struct.
pub type RawJanssonValue = jansson_sys::json_t;

bitflags! {
    /// Flags that can be passed to JSON decoding functions.
    pub struct JanssonDecodingFlags: usize {
        const JSON_REJECT_DUPLICATES = 0x0001;
        const JSON_DISABLE_EOF_CHECK = 0x0002;
        const JSON_DECODE_ANY = 0x0004;
        const JSON_DECODE_INT_AS_REAL = 0x0008;
        const JSON_ALLOW_NUL = 0x0010;
    }
}

bitflags! {
    /// Flags that can be passed to JSON encoding functions.
    pub struct JanssonEncodingFlags: usize {
        const JSON_COMPACT = 0x0020;
        const JSON_ENSURE_ASCII = 0x0040;
        const JSON_SORT_KEYS = 0x0080;
        const JSON_PRESERVE_ORDER = 0x0100;
        const JSON_ENCODE_ANY = 0x0200;
        const JSON_ESCAPE_SLASH = 0x0400;
        const JSON_EMBED = 0x0800;
    }
}

/// A safe wrapper for a Jansson JSON value. Automatically increases and decreases the refcount
/// of the underlying value when cloned/dropped.
pub struct JanssonValue {
    ptr: *mut RawJanssonValue,
}

impl JanssonValue {
    /// Creates a `JanssonValue` owning the given raw pointer, incrementing the reference count.
    pub unsafe fn from_and_incref(ptr: *mut RawJanssonValue) -> Option<Self> {
        jansson_sys::json_incref(ptr);
        Self::from_raw(ptr)
    }

    /// Creates a `JanssonValue` owning the given raw pointer. Does not increment the reference count, but the
    /// `JanssonValue`'s destructor will decrement the reference count.
    pub unsafe fn from_raw(ptr: *mut RawJanssonValue) -> Option<Self> {
        ptr.as_mut().map(|p| Self { ptr: p })
    }

    /// Transfers ownership of this value to this pointer. The consumer of the pointer is responsible for calling
    /// json_decref on it later.
    pub fn into_raw(self) -> *mut RawJanssonValue {
        unsafe { jansson_sys::json_incref(self.ptr) };
        self.ptr
    }

    /// Gets the reference backing this value without taking ownership.
    pub fn as_mut_ref(&mut self) -> &mut RawJanssonValue {
        unsafe { self.ptr.as_mut().unwrap() }
    }

    /// Decodes a JSON string slice into a Jansson value, returning an error if decoding fails.
    pub fn from_str(input: &str, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<Error + Send + Sync>> {
        Self::from_cstr(&CString::new(input)?, decoding_flags)
    }

    /// Decodes a JSON C-style string into a Jansson value, returning an error if decoding fails.
    pub fn from_cstr(input: &CStr, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<Error + Send + Sync>> {
        unsafe {
            let mut error: jansson_sys::json_error_t = mem::uninitialized();
            let result = jansson_sys::json_loads(input.as_ptr(), decoding_flags.bits(), &mut error as *mut _);
            match Self::from_raw(result) {
                Some(val) => Ok(val),
                None => {
                    let ptr = &error.text as *const _;
                    let len = libc::strlen(ptr);
                    let sli = slice::from_raw_parts(ptr as *mut u8, len);
                    Err(From::from(str::from_utf8(sli)?))
                }
            }
        }
    }

    /// Encodes this Jansson value as a JSON owned C-style string.
    pub fn to_libcstring(&self, encoding_flags: JanssonEncodingFlags) -> LibcString {
        unsafe {
            let json = jansson_sys::json_dumps(self.ptr, encoding_flags.bits());
            LibcString::from_chars(json).expect("Error writing JSON output from Jansson value :(")
        }
    }
}

impl fmt::Debug for JanssonValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "JanssonValue {{ {} }}", self.to_libcstring(JanssonEncodingFlags::empty()).to_string_lossy())
    }
}

impl Deref for JanssonValue {
    type Target = RawJanssonValue;

    fn deref(&self) -> &RawJanssonValue {
        unsafe { &*self.ptr }
    }
}

impl Clone for JanssonValue {
    fn clone(&self) -> Self {
        unsafe { jansson_sys::json_incref(self.ptr) };
        Self { ptr: self.ptr }
    }
}

impl Drop for JanssonValue {
    fn drop(&mut self) {
        unsafe { jansson_sys::json_decref(self.ptr) }
    }
}

unsafe impl Send for JanssonValue {}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn round_trip() {
        let json = r#"{"a": "alpha", "b": true, "c": false, "d": 42, "e": 1.25, "f": null, "g": [1, 2, 3]}"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty()).unwrap();
        assert_eq!(json, result.to_libcstring(JanssonEncodingFlags::empty()).to_str().unwrap());
    }

    #[test]
    fn produce_jansson_errors() {
        let json = r#"{"a":"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty());
        assert!(result.is_err());
    }
}
<commit_msg>Use `dyn` syntax for traits<commit_after>/// Utilities to work with Jansson JSON values, which are exposed in the Janus plugin API.
extern crate libc;

use jansson_sys;
use std::error::Error;
use std::fmt;
use std::ffi::{CStr, CString};
use std::mem;
use std::ops::Deref;
use std::slice;
use std::str;
use utils::LibcString;

/// A pointer to a raw Jansson value struct.
pub type RawJanssonValue = jansson_sys::json_t;

bitflags! {
    /// Flags that can be passed to JSON decoding functions.
    pub struct JanssonDecodingFlags: usize {
        const JSON_REJECT_DUPLICATES = 0x0001;
        const JSON_DISABLE_EOF_CHECK = 0x0002;
        const JSON_DECODE_ANY = 0x0004;
        const JSON_DECODE_INT_AS_REAL = 0x0008;
        const JSON_ALLOW_NUL = 0x0010;
    }
}

bitflags! {
    /// Flags that can be passed to JSON encoding functions.
    pub struct JanssonEncodingFlags: usize {
        const JSON_COMPACT = 0x0020;
        const JSON_ENSURE_ASCII = 0x0040;
        const JSON_SORT_KEYS = 0x0080;
        const JSON_PRESERVE_ORDER = 0x0100;
        const JSON_ENCODE_ANY = 0x0200;
        const JSON_ESCAPE_SLASH = 0x0400;
        const JSON_EMBED = 0x0800;
    }
}

/// A safe wrapper for a Jansson JSON value. Automatically increases and decreases the refcount
/// of the underlying value when cloned/dropped.
pub struct JanssonValue {
    ptr: *mut RawJanssonValue,
}

impl JanssonValue {
    /// Creates a `JanssonValue` owning the given raw pointer, incrementing the reference count.
    pub unsafe fn from_and_incref(ptr: *mut RawJanssonValue) -> Option<Self> {
        jansson_sys::json_incref(ptr);
        Self::from_raw(ptr)
    }

    /// Creates a `JanssonValue` owning the given raw pointer. Does not increment the reference count, but the
    /// `JanssonValue`'s destructor will decrement the reference count.
    pub unsafe fn from_raw(ptr: *mut RawJanssonValue) -> Option<Self> {
        ptr.as_mut().map(|p| Self { ptr: p })
    }

    /// Transfers ownership of this value to this pointer. The consumer of the pointer is responsible for calling
    /// json_decref on it later.
    pub fn into_raw(self) -> *mut RawJanssonValue {
        unsafe { jansson_sys::json_incref(self.ptr) };
        self.ptr
    }

    /// Gets the reference backing this value without taking ownership.
    pub fn as_mut_ref(&mut self) -> &mut RawJanssonValue {
        unsafe { self.ptr.as_mut().unwrap() }
    }

    /// Decodes a JSON string slice into a Jansson value, returning an error if decoding fails.
    pub fn from_str(input: &str, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
        Self::from_cstr(&CString::new(input)?, decoding_flags)
    }

    /// Decodes a JSON C-style string into a Jansson value, returning an error if decoding fails.
    pub fn from_cstr(input: &CStr, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
        unsafe {
            let mut error: jansson_sys::json_error_t = mem::uninitialized();
            let result = jansson_sys::json_loads(input.as_ptr(), decoding_flags.bits(), &mut error as *mut _);
            match Self::from_raw(result) {
                Some(val) => Ok(val),
                None => {
                    let ptr = &error.text as *const _;
                    let len = libc::strlen(ptr);
                    let sli = slice::from_raw_parts(ptr as *mut u8, len);
                    Err(From::from(str::from_utf8(sli)?))
                }
            }
        }
    }

    /// Encodes this Jansson value as a JSON owned C-style string.
    pub fn to_libcstring(&self, encoding_flags: JanssonEncodingFlags) -> LibcString {
        unsafe {
            let json = jansson_sys::json_dumps(self.ptr, encoding_flags.bits());
            LibcString::from_chars(json).expect("Error writing JSON output from Jansson value :(")
        }
    }
}

impl fmt::Debug for JanssonValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "JanssonValue {{ {} }}", self.to_libcstring(JanssonEncodingFlags::empty()).to_string_lossy())
    }
}

impl Deref for JanssonValue {
    type Target = RawJanssonValue;

    fn deref(&self) -> &RawJanssonValue {
        unsafe { &*self.ptr }
    }
}

impl Clone for JanssonValue {
    fn clone(&self) -> Self {
        unsafe { jansson_sys::json_incref(self.ptr) };
        Self { ptr: self.ptr }
    }
}

impl Drop for JanssonValue {
    fn drop(&mut self) {
        unsafe { jansson_sys::json_decref(self.ptr) }
    }
}

unsafe impl Send for JanssonValue {}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn round_trip() {
        let json = r#"{"a": "alpha", "b": true, "c": false, "d": 42, "e": 1.25, "f": null, "g": [1, 2, 3]}"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty()).unwrap();
        assert_eq!(json, result.to_libcstring(JanssonEncodingFlags::empty()).to_str().unwrap());
    }

    #[test]
    fn produce_jansson_errors() {
        let json = r#"{"a":"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty());
        assert!(result.is_err());
    }
}
<|endoftext|>


<commit_before>use std::vec::Vec;

pub struct Machine {
    memory: Vec<u32>,
    counter: u32,
}

impl Machine {
    pub fn new() -> Machine {
        Machine {
            memory: vec![0; 2097152],
            counter: 0,
        }
    }

    pub fn with_data(data: Vec<u32>) -> Machine {
        Machine {
            memory: data,
            counter: 0,
        }
    }

    pub fn loc(&self) -> u32 {
        self.counter
    }

    pub fn dump(&self) -> &Vec<u32> {
        &self.memory
    }

    pub fn load(&mut self, data: Vec<u32>) {
        self.memory = data;
    }

    pub fn step(&mut self) {
        let counter = self.counter as usize;
        let opcode = self.memory[counter];
        let a = self.memory[counter + 1];
        let b = self.memory[counter + 2];
        let c = self.memory[counter + 3];
        self.exec(opcode, a, b, c);
    }

    pub fn exec(&mut self, opcode: u32, a: u32, b: u32, c: u32) {
        match opcode {
            // PC <- M[A]
            1 => {
                self.counter = self.memory[a as usize];
            },

            // If M[B] = 0, then PC <- M[A]
            2 => {
                if 0 == self.memory[b as usize] {
                  self.counter = self.memory[a as usize];
                }
            },

            // M[A] <- PC
            3 => {
                self.memory[a as usize] = self.counter;
                self.counter += 4;
            },

            // M[A] <- M[B]
            4 => {
                self.memory[a as usize] = self.memory[b as usize];
                self.counter += 4;
            },

            // M[A] <- M[M[B]]
            5 => {
                let b = self.memory[b as usize];
                self.memory[a as usize] = self.memory[b as usize];
                self.counter += 4;
            },

            // M[M[B]] <- M[A]
            6 => {
                let b = self.memory[b as usize];
                self.memory[b as usize] = self.memory[a as usize];
                self.counter += 4;
            },

            // M[A] <- M[B] + M[C]
            7 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b + c;
                self.counter += 4;
            },

            // M[A] <- M[B] - M[C]
            8 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b - c;
                self.counter += 4;
            },

            // M[A] <- M[B] x M[C]
            9 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b * c;
                self.counter += 4;
            },

            // M[A] <- M[B] / M[C]
            10 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b / c;
                self.counter += 4;
            },

            // M[A] <- M[B] modulo M[C]
            11 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b % c;
                self.counter += 4;
            },

            // If M[B] < M[C], then M[A] <- 1, else M[A] <- 0
            12 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                if b < c {
                  self.memory[a as usize] = 1;
                } else {
                  self.memory[a as usize] = 0;
                }
                self.counter += 4;
            },

            // Unknown opcode
            _ => {
            },
        }
    }
}

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

    #[test]
    fn it_loads_data() {
        let mut m = Machine::new();
        assert_eq!(Some(&0), m.dump().first());

        m.load(vec![1]);
        assert_eq!(Some(&1), m.dump().first());
    }

    #[test]
    fn it_runs_opcode_1() {
        // PC <- M[A]
        let mut m = Machine::with_data(vec![1, 4, 0, 0, 2]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(2, m.loc());
    }

    #[test]
    fn it_runs_opcode_2_then_branch() {
        // If M[B] = 0, then PC <- M[A]
        let mut m = Machine::with_data(vec![2, 4, 5, 0, 2, 0]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(2, m.loc());
    }

    #[test]
    fn it_runs_opcode_2_else_branch() {
        // If M[B] = 0, then PC <- M[A]
        let mut m = Machine::with_data(vec![2, 4, 5, 0, 2, 1]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(0, m.loc());
    }

    #[test]
    fn it_runs_opcode_3() {
        // M[A] <- PC
        let mut m = Machine::with_data(vec![3, 4, 0, 0, 1]);
        let expected = vec![3, 4, 0, 0, 0];

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(&expected, m.dump());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_4() {
        let mut m = Machine::with_data(vec![1, 0]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(4, 0, 1, 0);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_5() {
        let mut m = Machine::with_data(vec![1, 2, 0]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(5, 0, 1, 0);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_6() {
        let mut m = Machine::with_data(vec![1, 0]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(6, 1, 1, 0);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_7() {
        let mut m = Machine::with_data(vec![1, 2, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(7, 0, 1, 2);
        assert_eq!(Some(&5), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_8() {
        let mut m = Machine::with_data(vec![0, 3, 2]);

        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(8, 0, 1, 2);
        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_9() {
        let mut m = Machine::with_data(vec![1, 2, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(9, 0, 1, 2);
        assert_eq!(Some(&6), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_10() {
        let mut m = Machine::with_data(vec![1, 6, 2]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(10, 0, 1, 2);
        assert_eq!(Some(&3), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_11() {
        let mut m = Machine::with_data(vec![1, 11, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(11, 0, 1, 2);
        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_12_then_branch() {
        let mut m = Machine::with_data(vec![2, 3, 4]);

        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(12, 0, 1, 2);
        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_12_else_branch() {
        let mut m = Machine::with_data(vec![2, 4, 3]);

        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(12, 0, 1, 2);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }
}
<commit_msg>Replace exec with step for opcode 4.<commit_after>use std::vec::Vec;

pub struct Machine {
    memory: Vec<u32>,
    counter: u32,
}

impl Machine {
    pub fn new() -> Machine {
        Machine {
            memory: vec![0; 2097152],
            counter: 0,
        }
    }

    pub fn with_data(data: Vec<u32>) -> Machine {
        Machine {
            memory: data,
            counter: 0,
        }
    }

    pub fn loc(&self) -> u32 {
        self.counter
    }

    pub fn dump(&self) -> &Vec<u32> {
        &self.memory
    }

    pub fn load(&mut self, data: Vec<u32>) {
        self.memory = data;
    }

    pub fn step(&mut self) {
        let counter = self.counter as usize;
        let opcode = self.memory[counter];
        let a = self.memory[counter + 1];
        let b = self.memory[counter + 2];
        let c = self.memory[counter + 3];
        self.exec(opcode, a, b, c);
    }

    pub fn exec(&mut self, opcode: u32, a: u32, b: u32, c: u32) {
        match opcode {
            // PC <- M[A]
            1 => {
                self.counter = self.memory[a as usize];
            },

            // If M[B] = 0, then PC <- M[A]
            2 => {
                if 0 == self.memory[b as usize] {
                  self.counter = self.memory[a as usize];
                }
            },

            // M[A] <- PC
            3 => {
                self.memory[a as usize] = self.counter;
                self.counter += 4;
            },

            // M[A] <- M[B]
            4 => {
                self.memory[a as usize] = self.memory[b as usize];
                self.counter += 4;
            },

            // M[A] <- M[M[B]]
            5 => {
                let b = self.memory[b as usize];
                self.memory[a as usize] = self.memory[b as usize];
                self.counter += 4;
            },

            // M[M[B]] <- M[A]
            6 => {
                let b = self.memory[b as usize];
                self.memory[b as usize] = self.memory[a as usize];
                self.counter += 4;
            },

            // M[A] <- M[B] + M[C]
            7 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b + c;
                self.counter += 4;
            },

            // M[A] <- M[B] - M[C]
            8 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b - c;
                self.counter += 4;
            },

            // M[A] <- M[B] x M[C]
            9 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b * c;
                self.counter += 4;
            },

            // M[A] <- M[B] / M[C]
            10 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b / c;
                self.counter += 4;
            },

            // M[A] <- M[B] modulo M[C]
            11 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                self.memory[a as usize] = b % c;
                self.counter += 4;
            },

            // If M[B] < M[C], then M[A] <- 1, else M[A] <- 0
            12 => {
                let b = self.memory[b as usize];
                let c = self.memory[c as usize];
                if b < c {
                  self.memory[a as usize] = 1;
                } else {
                  self.memory[a as usize] = 0;
                }
                self.counter += 4;
            },

            // Unknown opcode
            _ => {
            },
        }
    }
}

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

    #[test]
    fn it_loads_data() {
        let mut m = Machine::new();
        assert_eq!(Some(&0), m.dump().first());

        m.load(vec![1]);
        assert_eq!(Some(&1), m.dump().first());
    }

    #[test]
    fn it_runs_opcode_1() {
        // PC <- M[A]
        let mut m = Machine::with_data(vec![1, 4, 0, 0, 2]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(2, m.loc());
    }

    #[test]
    fn it_runs_opcode_2_then_branch() {
        // If M[B] = 0, then PC <- M[A]
        let mut m = Machine::with_data(vec![2, 4, 5, 0, 2, 0]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(2, m.loc());
    }

    #[test]
    fn it_runs_opcode_2_else_branch() {
        // If M[B] = 0, then PC <- M[A]
        let mut m = Machine::with_data(vec![2, 4, 5, 0, 2, 1]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(0, m.loc());
    }

    #[test]
    fn it_runs_opcode_3() {
        // M[A] <- PC
        let mut m = Machine::with_data(vec![3, 4, 0, 0, 1]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(&vec![3, 4, 0, 0, 0], m.dump());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_4() {
        // M[A] <- M[B]
        let mut m = Machine::with_data(vec![4, 4, 5, 0, 6, 7]);

        assert_eq!(0, m.loc());
        m.step();
        assert_eq!(&vec![4, 4, 5, 0, 7, 7], m.dump());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_5() {
        let mut m = Machine::with_data(vec![1, 2, 0]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(5, 0, 1, 0);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_6() {
        let mut m = Machine::with_data(vec![1, 0]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(6, 1, 1, 0);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_7() {
        let mut m = Machine::with_data(vec![1, 2, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(7, 0, 1, 2);
        assert_eq!(Some(&5), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_8() {
        let mut m = Machine::with_data(vec![0, 3, 2]);

        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(8, 0, 1, 2);
        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_9() {
        let mut m = Machine::with_data(vec![1, 2, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(9, 0, 1, 2);
        assert_eq!(Some(&6), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_10() {
        let mut m = Machine::with_data(vec![1, 6, 2]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(10, 0, 1, 2);
        assert_eq!(Some(&3), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_11() {
        let mut m = Machine::with_data(vec![1, 11, 3]);

        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(11, 0, 1, 2);
        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_12_then_branch() {
        let mut m = Machine::with_data(vec![2, 3, 4]);

        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(12, 0, 1, 2);
        assert_eq!(Some(&1), m.dump().first());
        assert_eq!(4, m.loc());
    }

    #[test]
    fn it_runs_opcode_12_else_branch() {
        let mut m = Machine::with_data(vec![2, 4, 3]);

        assert_eq!(Some(&2), m.dump().first());
        assert_eq!(0, m.loc());
        m.exec(12, 0, 1, 2);
        assert_eq!(Some(&0), m.dump().first());
        assert_eq!(4, m.loc());
    }
}
<|endoftext|>


<commit_before>extern crate dbus_bytestream;
use self::dbus_bytestream::message;

extern crate dbus_serialize;
use self::dbus_serialize::types::Variant;

use super::error::Error;
use super::value::{BasicValue, Marshal, Value};

#[derive(Debug)]
/// A message to communicate on the D-Bus.
pub struct Message {
    pub message: message::Message,
}

/// The type of a message.
pub enum MessageType {
    /// An error message.
    Error,
    /// A malformed message.
    Invalid,
    /// A message for a method call.
    MethodCall,
    /// A message for a return from a method call.
    MethodReturn,
    /// A signal.
    Signal,
}

impl Message {
    /// Create a new message from the underlying data type.
    pub fn new(message: message::Message) -> Self {
        Message {
            message: message,
        }
    }

    /// Create a call to a method.
    pub fn new_method_call(dest: &str, path: &str, iface: &str, method: &str) -> Self {
        Message {
            message: message::create_method_call(dest, path, iface, method),
        }
    }

    /// Create a signal message.
    pub fn new_signal(path: &str, iface: &str, method: &str) -> Self {
        Message {
            message: message::create_signal(path, iface, method),
        }
    }

    /// Create an error message.
    pub fn error_message(&self, name: &str) -> Self {
        Message {
            message: message::create_error(name, self.message.serial),
        }
    }

    /// Create a message which is a return value for the current message.
    ///
    /// This is used so that the return value is associated with the method call message.
    pub fn return_message(&self) -> Self {
        Message {
            message: message::create_method_return(self.message.serial),
        }
    }

    /// Add an argument to the message.
    pub fn add_argument(self, arg: &Marshal) -> Self {
        Message {
            message: self.message.add_arg(arg),
        }
    }

    /// The type of the message.
    pub fn message_type(&self) -> MessageType {
        match self.message.message_type {
            message::MESSAGE_TYPE_ERROR         => MessageType::Error,
            message::MESSAGE_TYPE_INVALID       => MessageType::Invalid,
            message::MESSAGE_TYPE_METHOD_CALL   => MessageType::MethodCall,
            message::MESSAGE_TYPE_METHOD_RETURN => MessageType::MethodReturn,
            message::MESSAGE_TYPE_SIGNAL        => MessageType::Signal,
            _                                   => MessageType::Invalid,
        }
    }

    fn _extract_string(v: &Variant) -> Option<String> {
        if let Value::BasicValue(BasicValue::String(ref s)) = *v.object {
            Some(s.clone())
        } else {
            None
        }
    }

    fn _get_header_string(message: &message::Message, header: u8) -> Option<String> {
        message.get_header(header)
            .and_then(Self::_extract_string)
    }

    /// The interface the message is destined for.
    pub fn interface(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_INTERFACE)
    }

    /// The object path the message is destined for.
    pub fn path(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_PATH)
    }

    /// The method or signal name the message is associated with.
    pub fn member(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_MEMBER)
    }

    /// Unpack the argument values stored within the message.
    pub fn values(&self) -> Result<Option<Vec<Value>>, Error> {
        Ok(try!(self.message.get_body()))
    }
}
<commit_msg>message: hide the `message` member<commit_after>extern crate dbus_bytestream;
use self::dbus_bytestream::message;

extern crate dbus_serialize;
use self::dbus_serialize::types::Variant;

use super::error::Error;
use super::value::{BasicValue, Marshal, Value};

#[derive(Debug)]
/// A message to communicate on the D-Bus.
pub struct Message {
    #[doc(hidden)]
    // This is used inside of the implementation, but should not be fully public.
    pub message: message::Message,
}

/// The type of a message.
pub enum MessageType {
    /// An error message.
    Error,
    /// A malformed message.
    Invalid,
    /// A message for a method call.
    MethodCall,
    /// A message for a return from a method call.
    MethodReturn,
    /// A signal.
    Signal,
}

impl Message {
    /// Create a new message from the underlying data type.
    pub fn new(message: message::Message) -> Self {
        Message {
            message: message,
        }
    }

    /// Create a call to a method.
    pub fn new_method_call(dest: &str, path: &str, iface: &str, method: &str) -> Self {
        Message {
            message: message::create_method_call(dest, path, iface, method),
        }
    }

    /// Create a signal message.
    pub fn new_signal(path: &str, iface: &str, method: &str) -> Self {
        Message {
            message: message::create_signal(path, iface, method),
        }
    }

    /// Create an error message.
    pub fn error_message(&self, name: &str) -> Self {
        Message {
            message: message::create_error(name, self.message.serial),
        }
    }

    /// Create a message which is a return value for the current message.
    ///
    /// This is used so that the return value is associated with the method call message.
    pub fn return_message(&self) -> Self {
        Message {
            message: message::create_method_return(self.message.serial),
        }
    }

    /// Add an argument to the message.
    pub fn add_argument(self, arg: &Marshal) -> Self {
        Message {
            message: self.message.add_arg(arg),
        }
    }

    /// The type of the message.
    pub fn message_type(&self) -> MessageType {
        match self.message.message_type {
            message::MESSAGE_TYPE_ERROR         => MessageType::Error,
            message::MESSAGE_TYPE_INVALID       => MessageType::Invalid,
            message::MESSAGE_TYPE_METHOD_CALL   => MessageType::MethodCall,
            message::MESSAGE_TYPE_METHOD_RETURN => MessageType::MethodReturn,
            message::MESSAGE_TYPE_SIGNAL        => MessageType::Signal,
            _                                   => MessageType::Invalid,
        }
    }

    fn _extract_string(v: &Variant) -> Option<String> {
        if let Value::BasicValue(BasicValue::String(ref s)) = *v.object {
            Some(s.clone())
        } else {
            None
        }
    }

    fn _get_header_string(message: &message::Message, header: u8) -> Option<String> {
        message.get_header(header)
            .and_then(Self::_extract_string)
    }

    /// The interface the message is destined for.
    pub fn interface(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_INTERFACE)
    }

    /// The object path the message is destined for.
    pub fn path(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_PATH)
    }

    /// The method or signal name the message is associated with.
    pub fn member(&self) -> Option<String> {
        Self::_get_header_string(&self.message, message::HEADER_FIELD_MEMBER)
    }

    /// Unpack the argument values stored within the message.
    pub fn values(&self) -> Result<Option<Vec<Value>>, Error> {
        Ok(try!(self.message.get_body()))
    }
}
<|endoftext|>