<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Local state for external blobs and their states.

use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};

use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use sodiumoxide::randombytes::randombytes;

use errors::DieselError;
use tags;
use util;

use super::schema;

#[derive(Clone, Debug, Default)]
pub struct BlobDesc {
    pub name: Vec<u8>,
    pub id: i64,
}

pub struct InternalBlobIndex {
    conn: SqliteConnection,
    next_id: i64,
    reserved: HashMap<Vec<u8>, BlobDesc>,
}

#[derive(Clone)]
pub struct BlobIndex(Arc<Mutex<InternalBlobIndex>>);

impl InternalBlobIndex {
    pub fn new(path: &str) -> Result<InternalBlobIndex, DieselError> {
        let conn = try!(SqliteConnection::establish(path));

        let mut bi = InternalBlobIndex {
            conn: conn,
            next_id: -1,
            reserved: HashMap::new(),
        };

        let dir = try!(diesel::migrations::find_migrations_directory());
        try!(diesel::migrations::run_pending_migrations_in_directory(&bi.conn,
                                                                     &dir,
                                                                     &mut util::InfoWriter));
        try!(bi.conn.begin_transaction());
        bi.refresh_next_id();
        Ok(bi)
    }

    fn new_blob_desc(&mut self) -> BlobDesc {
        BlobDesc {
            name: randombytes(24),
            id: self.next_id(),
        }
    }

    fn refresh_next_id(&mut self) {
        use diesel::expression::max;
        use super::schema::blobs::dsl::*;

        let id_opt = blobs.select(max(id).nullable())
            .first::<Option<i64>>(&self.conn)
            .optional()
            .expect("Error querying blobs")
            .and_then(|x| x);

        self.next_id = 1 + id_opt.unwrap_or(0);
    }

    fn next_id(&mut self) -> i64 {
        let id = self.next_id;
        self.next_id += 1;

        id
    }

    fn recover(&mut self, name: Vec<u8>) -> BlobDesc {
        if let Some(id) = self.find_id(&name[..]) {
            // Blob exists.
            return BlobDesc {
                name: name,
                id: id,
            };
        }

        let blob = BlobDesc {
            name: name,
            id: self.next_id(),
        };
        self.reserved.insert(blob.name.clone(), blob.clone());
        self.in_air(&blob);
        self.commit_blob(&blob);

        blob
    }

    fn reserve(&mut self) -> BlobDesc {
        let blob = self.new_blob_desc();
        self.reserved.insert(blob.name.clone(), blob.clone());

        blob
    }

    fn in_air(&mut self, blob: &BlobDesc) {
        assert!(self.reserved.get(&blob.name).is_some(),
                "blob was not reserved!");
        use super::schema::blobs::dsl::*;

        let new = schema::NewBlob {
            id: blob.id,
            name: &blob.name,
            tag: tags::Tag::InProgress as i32,
        };
        diesel::insert(&new)
            .into(blobs)
            .execute(&self.conn)
            .expect("Error inserting blob");

        self.new_transaction();
    }

    fn new_transaction(&mut self) {
        self.conn.commit_transaction().unwrap();
        self.conn.begin_transaction().unwrap();
    }

    fn commit_blob(&mut self, blob: &BlobDesc) {
        assert!(self.reserved.get(&blob.name).is_some(),
                "blob was not reserved!");
        use super::schema::blobs::dsl::*;

        diesel::update(blobs.find(blob.id))
            .set(tag.eq(tags::Tag::Done as i32))
            .execute(&self.conn)
            .expect("Error updating blob");
        self.new_transaction();
    }

    fn find_id(&mut self, name_: &[u8]) -> Option<i64> {
        use super::schema::blobs::dsl::*;
        blobs.filter(name.eq(name_))
            .select(id)
            .first::<i64>(&self.conn)
            .optional()
            .expect("Error reading blob")
    }

    fn tag(&mut self, tag_: tags::Tag, target: Option<&BlobDesc>) {
        use super::schema::blobs::dsl::*;
        match target {
            None => {
                diesel::update(blobs)
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            Some(ref t) if t.id > 0 => {
                diesel::update(blobs.find(t.id))
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            Some(ref t) if !t.name.is_empty() => {
                diesel::update(blobs.filter(name.eq(&t.name)))
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            _ => unreachable!(),
        };
    }

    fn delete_by_tag(&mut self, tag_: tags::Tag) {
        use super::schema::blobs::dsl::*;
        diesel::delete(blobs.filter(tag.eq(tag_ as i32)))
            .execute(&self.conn)
            .expect("Error deleting blobs");
    }

    fn list_by_tag(&mut self, tag_: tags::Tag) -> Vec<BlobDesc> {
        use super::schema::blobs::dsl::*;
        blobs.filter(tag.eq(tag_ as i32))
            .load::<schema::Blob>(&self.conn)
            .expect("Error listing blobs")
            .into_iter()
            .map(|blob_| {
                BlobDesc {
                    id: blob_.id,
                    name: blob_.name,
                }
            })
            .collect()
    }
}

impl BlobIndex {
    pub fn new(path: &str) -> Result<BlobIndex, DieselError> {
        InternalBlobIndex::new(path).map(|index| BlobIndex(Arc::new(Mutex::new(index))))
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Result<BlobIndex, DieselError> {
        BlobIndex::new(":memory:")
    }

    fn lock(&self) -> MutexGuard<InternalBlobIndex> {
        self.0.lock().expect("index-process has failed")
    }

    /// Reserve an internal `BlobDesc` for a new blob.
    pub fn reserve(&self) -> BlobDesc {
        self.lock().reserve()
    }

    /// Report that this blob is in the process of being committed to persistent storage. If a
    /// blob is in this state when the system starts up, it may or may not exist in the persistent
    /// storage, but **should not** be referenced elsewhere, and is therefore safe to delete.
    pub fn in_air(&self, blob: &BlobDesc) {
        self.lock().in_air(&blob)
    }

    /// Report that this blob has been fully committed to persistent storage. We can now use its
    /// reference internally. Only committed blobs are considered "safe to use".
    pub fn commit_done(&self, blob: &BlobDesc) {
        self.lock().commit_blob(blob)
    }

    /// Reinstall blob recovered by from external storage.
    /// Creates a new blob by a known external name.
    pub fn recover(&self, name: Vec<u8>) -> BlobDesc {
        self.lock().recover(name)
    }

    pub fn tag(&self, blob: &BlobDesc, tag: tags::Tag) {
        self.lock().tag(tag, Some(blob))
    }

    pub fn tag_all(&self, tag: tags::Tag) {
        self.lock().tag(tag, None)
    }

    pub fn list_by_tag(&self, tag: tags::Tag) -> Vec<BlobDesc> {
        self.lock().list_by_tag(tag)
    }

    pub fn delete_by_tag(&self, tag: tags::Tag) {
        self.lock().delete_by_tag(tag)
    }

    pub fn flush(&self) {
        self.lock().new_transaction()
    }
}
<commit_msg>Remove list of reserved blobs.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Local state for external blobs and their states.

use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};

use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use sodiumoxide::randombytes::randombytes;

use errors::DieselError;
use tags;
use util;

use super::schema;

#[derive(Clone, Debug, Default)]
pub struct BlobDesc {
    pub name: Vec<u8>,
    pub id: i64,
}

pub struct InternalBlobIndex {
    conn: SqliteConnection,
    next_id: i64,
}

#[derive(Clone)]
pub struct BlobIndex(Arc<Mutex<InternalBlobIndex>>);

impl InternalBlobIndex {
    pub fn new(path: &str) -> Result<InternalBlobIndex, DieselError> {
        let conn = try!(SqliteConnection::establish(path));

        let mut bi = InternalBlobIndex {
            conn: conn,
            next_id: -1,
        };

        let dir = try!(diesel::migrations::find_migrations_directory());
        try!(diesel::migrations::run_pending_migrations_in_directory(&bi.conn,
                                                                     &dir,
                                                                     &mut util::InfoWriter));
        try!(bi.conn.begin_transaction());
        bi.refresh_next_id();
        Ok(bi)
    }

    fn new_blob_desc(&mut self) -> BlobDesc {
        BlobDesc {
            name: randombytes(24),
            id: self.next_id(),
        }
    }

    fn refresh_next_id(&mut self) {
        use diesel::expression::max;
        use super::schema::blobs::dsl::*;

        let id_opt = blobs.select(max(id).nullable())
            .first::<Option<i64>>(&self.conn)
            .optional()
            .expect("Error querying blobs")
            .and_then(|x| x);

        self.next_id = 1 + id_opt.unwrap_or(0);
    }

    fn next_id(&mut self) -> i64 {
        let id = self.next_id;
        self.next_id += 1;

        id
    }

    fn recover(&mut self, name: Vec<u8>) -> BlobDesc {
        if let Some(id) = self.find_id(&name[..]) {
            // Blob exists.
            return BlobDesc {
                name: name,
                id: id,
            };
        }

        let blob = BlobDesc {
            name: name,
            id: self.next_id(),
        };
        self.in_air(&blob);
        self.commit_blob(&blob);

        blob
    }

    fn reserve(&mut self) -> BlobDesc {
        self.new_blob_desc()
    }

    fn in_air(&mut self, blob: &BlobDesc) {
        use super::schema::blobs::dsl::*;

        let new = schema::NewBlob {
            id: blob.id,
            name: &blob.name,
            tag: tags::Tag::InProgress as i32,
        };
        diesel::insert(&new)
            .into(blobs)
            .execute(&self.conn)
            .expect("Error inserting blob");

        self.new_transaction();
    }

    fn new_transaction(&mut self) {
        self.conn.commit_transaction().unwrap();
        self.conn.begin_transaction().unwrap();
    }

    fn commit_blob(&mut self, blob: &BlobDesc) {
        use super::schema::blobs::dsl::*;

        diesel::update(blobs.find(blob.id))
            .set(tag.eq(tags::Tag::Done as i32))
            .execute(&self.conn)
            .expect("Error updating blob");
        self.new_transaction();
    }

    fn find_id(&mut self, name_: &[u8]) -> Option<i64> {
        use super::schema::blobs::dsl::*;
        blobs.filter(name.eq(name_))
            .select(id)
            .first::<i64>(&self.conn)
            .optional()
            .expect("Error reading blob")
    }

    fn tag(&mut self, tag_: tags::Tag, target: Option<&BlobDesc>) {
        use super::schema::blobs::dsl::*;
        match target {
            None => {
                diesel::update(blobs)
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            Some(ref t) if t.id > 0 => {
                diesel::update(blobs.find(t.id))
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            Some(ref t) if !t.name.is_empty() => {
                diesel::update(blobs.filter(name.eq(&t.name)))
                    .set(tag.eq(tag_ as i32))
                    .execute(&self.conn)
                    .expect("Error updating blob tags")
            }
            _ => unreachable!(),
        };
    }

    fn delete_by_tag(&mut self, tag_: tags::Tag) {
        use super::schema::blobs::dsl::*;
        diesel::delete(blobs.filter(tag.eq(tag_ as i32)))
            .execute(&self.conn)
            .expect("Error deleting blobs");
    }

    fn list_by_tag(&mut self, tag_: tags::Tag) -> Vec<BlobDesc> {
        use super::schema::blobs::dsl::*;
        blobs.filter(tag.eq(tag_ as i32))
            .load::<schema::Blob>(&self.conn)
            .expect("Error listing blobs")
            .into_iter()
            .map(|blob_| {
                BlobDesc {
                    id: blob_.id,
                    name: blob_.name,
                }
            })
            .collect()
    }
}

impl BlobIndex {
    pub fn new(path: &str) -> Result<BlobIndex, DieselError> {
        InternalBlobIndex::new(path).map(|index| BlobIndex(Arc::new(Mutex::new(index))))
    }

    #[cfg(test)]
    pub fn new_for_testing() -> Result<BlobIndex, DieselError> {
        BlobIndex::new(":memory:")
    }

    fn lock(&self) -> MutexGuard<InternalBlobIndex> {
        self.0.lock().expect("index-process has failed")
    }

    /// Reserve an internal `BlobDesc` for a new blob.
    pub fn reserve(&self) -> BlobDesc {
        self.lock().reserve()
    }

    /// Report that this blob is in the process of being committed to persistent storage. If a
    /// blob is in this state when the system starts up, it may or may not exist in the persistent
    /// storage, but **should not** be referenced elsewhere, and is therefore safe to delete.
    pub fn in_air(&self, blob: &BlobDesc) {
        self.lock().in_air(&blob)
    }

    /// Report that this blob has been fully committed to persistent storage. We can now use its
    /// reference internally. Only committed blobs are considered "safe to use".
    pub fn commit_done(&self, blob: &BlobDesc) {
        self.lock().commit_blob(blob)
    }

    /// Reinstall blob recovered by from external storage.
    /// Creates a new blob by a known external name.
    pub fn recover(&self, name: Vec<u8>) -> BlobDesc {
        self.lock().recover(name)
    }

    pub fn tag(&self, blob: &BlobDesc, tag: tags::Tag) {
        self.lock().tag(tag, Some(blob))
    }

    pub fn tag_all(&self, tag: tags::Tag) {
        self.lock().tag(tag, None)
    }

    pub fn list_by_tag(&self, tag: tags::Tag) -> Vec<BlobDesc> {
        self.lock().list_by_tag(tag)
    }

    pub fn delete_by_tag(&self, tag: tags::Tag) {
        self.lock().delete_by_tag(tag)
    }

    pub fn flush(&self) {
        self.lock().new_transaction()
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate diesel;

use diesel::*;
use diesel::pg::Pg;
use diesel::sqlite::SqliteConnection;

table! {
    users {
        id -> Integer,
    }
}

fn main() {
    let connection = SqliteConnection::establish("").unwrap();
    users::table.into_boxed::<Pg>().load::<i32>(&connection);
    //~^ ERROR type mismatch
}
<commit_msg>Fix travis build<commit_after>#[macro_use]
extern crate diesel;

use diesel::*;
use diesel::pg::Pg;
use diesel::sqlite::SqliteConnection;

table! {
    users {
        id -> Integer,
    }
}

fn main() {
    let connection = SqliteConnection::establish("").unwrap();
    users::table.into_boxed::<Pg>().load::<(i32,)>(&connection);
    //~^ ERROR type mismatch
}
<|endoftext|>


<commit_before>#[macro_use]
mod generation;

use chrono::{Datelike, Local};
use feed::Feed;
use std::path::Path;
use yaml_rust::{Yaml, YamlLoader};

create_config_struct!(Spike,
    jump:                    f32 => "Jump Required"                        => 0.3,
    low_listener_increase:   f32 => "Low Listener Increase"                => [0.0, 0.005],
    high_listener_dec:       f32 => "High Listener Decrease"               => [0.0, 0.02],
    high_listener_dec_every: f32 => "High Listener Decrease Per Listeners" => [1.0, 100.0],
);

create_config_struct!(UnskewedAverage,
    reset_pcnt:      f32 => "Reset To Average Percentage"  => [0.0, 0.15],
    adjust_pcnt:     f32 => "Adjust to Average Percentage" => [0.0, 0.0075],
    spikes_required: u32 => "Spikes Required"              => 1,
    jump_required:   f32 => "Jump Required To Set"         => [1.1, 4.0],
);

create_config_enum!(FeedIdent,
    Name(String)   => self,
    ID(u32)        => self,
    County(String) => self,
    State(u32)     => "State ID",
);

impl FeedIdent {
    /// Returns true if the FeedIdent matches the corresponding feed data.
    pub fn matches_feed(&self, feed: &Feed) -> bool {
        match *self {
            FeedIdent::Name(ref name) => *name == feed.name,
            FeedIdent::ID(id) => id == feed.id,
            FeedIdent::County(ref c) => *c == feed.county,
            FeedIdent::State(id) => id == feed.state.id,
        }
    }
}

create_config_enum!(WeekdaySpike,
    Sunday(Spike)    => self,
    Monday(Spike)    => self,
    Tuesday(Spike)   => self,
    Wednesday(Spike) => self,
    Thursday(Spike)  => self,
    Friday(Spike)    => self,
    Saturday(Spike)  => self,
);

impl WeekdaySpike {
    /// Returns the spike values for the current day, if it exists in the specified array.
    pub fn get_for_today(weekday_spikes: &[WeekdaySpike]) -> Option<&Spike> {
        use chrono::Weekday::*;
        use self::WeekdaySpike::*;

        let weekday = Local::today().weekday();

        for ws in weekday_spikes {
            match (weekday, ws) {
                (Mon, &Monday(ref s))
                | (Tue, &Tuesday(ref s))
                | (Wed, &Wednesday(ref s))
                | (Thu, &Thursday(ref s))
                | (Fri, &Friday(ref s))
                | (Sat, &Saturday(ref s))
                | (Sun, &Sunday(ref s)) => return Some(s),
                _ => (),
            }
        }

        None
    }
}

create_config_struct!(FeedSetting,
    ident:          FeedIdent         => self                        => fail,
    spike:          Spike             => "Spike Percentages"         => default,
    weekday_spikes: Vec<WeekdaySpike> => "Weekday Spike Percentages" => all,
);

create_config_struct!(Misc,
	update_time:       f32         => "Update Time"              => [5.0, 6.0],
	minimum_listeners: u32         => "Minimum Listeners"        => 15,
	state_feeds_id:    Option<u32> => "State Feeds ID"           => None,
    max_feeds:         u32         => "Maximum Feeds To Display" => 10,
);

create_config_enum!(SortType,
    Listeners => self,
    Jump      => self,
);

create_config_enum!(SortOrder,
    Ascending  => self,
    Descending => self,
);

create_config_struct!(Sorting,
    sort_type:  SortType  => "Sort By"    => { SortType::Listeners },
    sort_order: SortOrder => "Sort Order" => { SortOrder::Descending },
);

macro_rules! gen_base_parse_stmt {
    (optional, $category:expr, $doc:ident) => (ParseYaml::from(&$doc[$category]));
    (default,  $category:expr, $doc:ident) => (ParseYaml::from_or_default(&$doc[$category]));
    (all,      $category:expr, $doc:ident) => (ParseYaml::all(&$doc[$category]));
}

macro_rules! gen_base_config {
    ($name:ident, $($field:ident: $type:ty => $parse_type:ident => $category:expr,)+) => {
        #[derive(Debug, Default)]
        pub struct $name {
            $(pub $field: $type,)+
        }

        impl $name {
            pub fn from_file(path: &Path) -> Result<$name, ::error::ConfigError> {
                let file = ::util::read_file(path).map_err(::error::ConfigError::Io)?;

                if file.len() == 0 {
                    return Ok(Config::default())
                }

                let doc = YamlLoader::load_from_str(&file).map_err(::error::ConfigError::YAMLScan)?;
                let doc = &doc[0]; // We only care about the first document

                Ok($name {
                    $($field: gen_base_parse_stmt!($parse_type, $category, doc),)+
                })
            }
        }
    };
}

gen_base_config!(Config,
    global_spike:   Spike             => default => "Spike Percentage",
    unskewed_avg:   UnskewedAverage   => default => "Unskewed Average",
    weekday_spikes: Vec<WeekdaySpike> => all     => "Weekday Spike Percentages",
    feed_settings:  Vec<FeedSetting>  => all     => "Feed Settings",
    misc:           Misc              => default => "Misc",
    sorting:        Sorting           => default => "Feed Sorting",
    blacklist:      Vec<FeedIdent>    => all     => "Blacklist",
    whitelist:      Vec<FeedIdent>    => all     => "Whitelist",
);

impl Config {
    /// Gets the spike values for the specified feed based off of
    /// other configuration values that may be set.
    pub fn get_feed_spike(&self, feed: &Feed) -> &Spike {
        // Find any settings for the specified feed
        let feed_setting = self.feed_settings
            .iter()
            .find(|s| s.ident.matches_feed(feed));

        match feed_setting {
            Some(setting) => {
                WeekdaySpike::get_for_today(&setting.weekday_spikes).unwrap_or(&setting.spike)
            }
            None => WeekdaySpike::get_for_today(&self.weekday_spikes).unwrap_or(&self.global_spike),
        }
    }
}

trait ParseYaml: Sized + Default {
    fn from(doc: &Yaml) -> Option<Self>;

    fn from_or_default(doc: &Yaml) -> Self {
        ParseYaml::from(doc).unwrap_or_default()
    }

    fn all(doc: &Yaml) -> Vec<Self> {
        doc.as_vec()
            .unwrap_or(&Vec::new())
            .iter()
            .filter_map(ParseYaml::from)
            .collect()
    }
}

macro_rules! impl_parseyaml_for_numeric {
    ($($t:ty )+) => {
        $(
        impl ParseYaml for $t {
            fn from(doc: &Yaml) -> Option<$t> {
                use yaml_rust::Yaml::*;
                match *doc {
                    Integer(num)     => Some(num as $t),
                    Real(ref string) => string.parse().ok(),
                    _                => None,
                }
            }
        }
        )+
    }
}

impl_parseyaml_for_numeric!(u8 u32 f32);

impl ParseYaml for String {
    fn from(doc: &Yaml) -> Option<String> {
        use yaml_rust::Yaml::*;
        match *doc {
            String(ref s) => Some(s.clone()),
            _ => None,
        }
    }
}
<commit_msg>Bump default jump required to 0.4<commit_after>#[macro_use]
mod generation;

use chrono::{Datelike, Local};
use feed::Feed;
use std::path::Path;
use yaml_rust::{Yaml, YamlLoader};

create_config_struct!(Spike,
    jump:                    f32 => "Jump Required"                        => 0.4,
    low_listener_increase:   f32 => "Low Listener Increase"                => [0.0, 0.005],
    high_listener_dec:       f32 => "High Listener Decrease"               => [0.0, 0.02],
    high_listener_dec_every: f32 => "High Listener Decrease Per Listeners" => [1.0, 100.0],
);

create_config_struct!(UnskewedAverage,
    reset_pcnt:      f32 => "Reset To Average Percentage"  => [0.0, 0.15],
    adjust_pcnt:     f32 => "Adjust to Average Percentage" => [0.0, 0.0075],
    spikes_required: u32 => "Spikes Required"              => 1,
    jump_required:   f32 => "Jump Required To Set"         => [1.1, 4.0],
);

create_config_enum!(FeedIdent,
    Name(String)   => self,
    ID(u32)        => self,
    County(String) => self,
    State(u32)     => "State ID",
);

impl FeedIdent {
    /// Returns true if the FeedIdent matches the corresponding feed data.
    pub fn matches_feed(&self, feed: &Feed) -> bool {
        match *self {
            FeedIdent::Name(ref name) => *name == feed.name,
            FeedIdent::ID(id) => id == feed.id,
            FeedIdent::County(ref c) => *c == feed.county,
            FeedIdent::State(id) => id == feed.state.id,
        }
    }
}

create_config_enum!(WeekdaySpike,
    Sunday(Spike)    => self,
    Monday(Spike)    => self,
    Tuesday(Spike)   => self,
    Wednesday(Spike) => self,
    Thursday(Spike)  => self,
    Friday(Spike)    => self,
    Saturday(Spike)  => self,
);

impl WeekdaySpike {
    /// Returns the spike values for the current day, if it exists in the specified array.
    pub fn get_for_today(weekday_spikes: &[WeekdaySpike]) -> Option<&Spike> {
        use chrono::Weekday::*;
        use self::WeekdaySpike::*;

        let weekday = Local::today().weekday();

        for ws in weekday_spikes {
            match (weekday, ws) {
                (Mon, &Monday(ref s))
                | (Tue, &Tuesday(ref s))
                | (Wed, &Wednesday(ref s))
                | (Thu, &Thursday(ref s))
                | (Fri, &Friday(ref s))
                | (Sat, &Saturday(ref s))
                | (Sun, &Sunday(ref s)) => return Some(s),
                _ => (),
            }
        }

        None
    }
}

create_config_struct!(FeedSetting,
    ident:          FeedIdent         => self                        => fail,
    spike:          Spike             => "Spike Percentages"         => default,
    weekday_spikes: Vec<WeekdaySpike> => "Weekday Spike Percentages" => all,
);

create_config_struct!(Misc,
	update_time:       f32         => "Update Time"              => [5.0, 6.0],
	minimum_listeners: u32         => "Minimum Listeners"        => 15,
	state_feeds_id:    Option<u32> => "State Feeds ID"           => None,
    max_feeds:         u32         => "Maximum Feeds To Display" => 10,
);

create_config_enum!(SortType,
    Listeners => self,
    Jump      => self,
);

create_config_enum!(SortOrder,
    Ascending  => self,
    Descending => self,
);

create_config_struct!(Sorting,
    sort_type:  SortType  => "Sort By"    => { SortType::Listeners },
    sort_order: SortOrder => "Sort Order" => { SortOrder::Descending },
);

macro_rules! gen_base_parse_stmt {
    (optional, $category:expr, $doc:ident) => (ParseYaml::from(&$doc[$category]));
    (default,  $category:expr, $doc:ident) => (ParseYaml::from_or_default(&$doc[$category]));
    (all,      $category:expr, $doc:ident) => (ParseYaml::all(&$doc[$category]));
}

macro_rules! gen_base_config {
    ($name:ident, $($field:ident: $type:ty => $parse_type:ident => $category:expr,)+) => {
        #[derive(Debug, Default)]
        pub struct $name {
            $(pub $field: $type,)+
        }

        impl $name {
            pub fn from_file(path: &Path) -> Result<$name, ::error::ConfigError> {
                let file = ::util::read_file(path).map_err(::error::ConfigError::Io)?;

                if file.len() == 0 {
                    return Ok(Config::default())
                }

                let doc = YamlLoader::load_from_str(&file).map_err(::error::ConfigError::YAMLScan)?;
                let doc = &doc[0]; // We only care about the first document

                Ok($name {
                    $($field: gen_base_parse_stmt!($parse_type, $category, doc),)+
                })
            }
        }
    };
}

gen_base_config!(Config,
    global_spike:   Spike             => default => "Spike Percentage",
    unskewed_avg:   UnskewedAverage   => default => "Unskewed Average",
    weekday_spikes: Vec<WeekdaySpike> => all     => "Weekday Spike Percentages",
    feed_settings:  Vec<FeedSetting>  => all     => "Feed Settings",
    misc:           Misc              => default => "Misc",
    sorting:        Sorting           => default => "Feed Sorting",
    blacklist:      Vec<FeedIdent>    => all     => "Blacklist",
    whitelist:      Vec<FeedIdent>    => all     => "Whitelist",
);

impl Config {
    /// Gets the spike values for the specified feed based off of
    /// other configuration values that may be set.
    pub fn get_feed_spike(&self, feed: &Feed) -> &Spike {
        // Find any settings for the specified feed
        let feed_setting = self.feed_settings
            .iter()
            .find(|s| s.ident.matches_feed(feed));

        match feed_setting {
            Some(setting) => {
                WeekdaySpike::get_for_today(&setting.weekday_spikes).unwrap_or(&setting.spike)
            }
            None => WeekdaySpike::get_for_today(&self.weekday_spikes).unwrap_or(&self.global_spike),
        }
    }
}

trait ParseYaml: Sized + Default {
    fn from(doc: &Yaml) -> Option<Self>;

    fn from_or_default(doc: &Yaml) -> Self {
        ParseYaml::from(doc).unwrap_or_default()
    }

    fn all(doc: &Yaml) -> Vec<Self> {
        doc.as_vec()
            .unwrap_or(&Vec::new())
            .iter()
            .filter_map(ParseYaml::from)
            .collect()
    }
}

macro_rules! impl_parseyaml_for_numeric {
    ($($t:ty )+) => {
        $(
        impl ParseYaml for $t {
            fn from(doc: &Yaml) -> Option<$t> {
                use yaml_rust::Yaml::*;
                match *doc {
                    Integer(num)     => Some(num as $t),
                    Real(ref string) => string.parse().ok(),
                    _                => None,
                }
            }
        }
        )+
    }
}

impl_parseyaml_for_numeric!(u8 u32 f32);

impl ParseYaml for String {
    fn from(doc: &Yaml) -> Option<String> {
        use yaml_rust::Yaml::*;
        match *doc {
            String(ref s) => Some(s.clone()),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::collections::{VecDeque, HashMap, HashSet};

use rand::{self, Rng, ChaChaRng};

use ogg::{OggTrack, OggTrackBuf};
use ogg::vorbis::{Comments, VorbisHeader};
use ireul_interface::proxy::track::model::{self, Handle};

struct HandleAllocator<R> {
    rng: R,
    limit: usize,
    allocated: HashSet<u64>,
}

impl<R> HandleAllocator<R> where R: Rng {
    pub fn new(rng: R, limit: usize) -> HandleAllocator<R> {
        HandleAllocator {
            rng: rng,
            limit: limit,
            allocated: HashSet::new(),
        }
    }

    pub fn generate(&mut self) -> Result<Handle, ()> {
        if self.limit <= self.allocated.len() {
            return Err(());
        }

        let new_handle;
        loop {
            let foo = self.rng.next_u64();
            if !self.allocated.contains(&foo) {
                new_handle = foo;
                break;
            }
        }

        println!("EMITTING HANDLE {:#x}", new_handle);
        self.allocated.insert(new_handle);
        Ok(Handle(new_handle))
    }

    pub fn dispose(&mut self, handle: Handle) -> Result<(), Handle> {
        match self.allocated.remove(&handle.0) {
            true => Ok(()),
            false => Err(handle),
        }
    }
}

#[derive(Clone)]
pub struct Track {
    handle: Handle,
    data: OggTrackBuf,
    comments: Comments,

    artist: String,
    album: String,
    title: String,

    sample_rate: u64,
    sample_count: u64,
}

impl Track {
    pub fn from_ogg_track(handle: Handle, ogg: OggTrackBuf) -> Track {
        use std::ascii::AsciiExt;

        let id_header = match VorbisHeader::find_identification(ogg.pages()) {
            Ok(header) => header.identification_header(),
            Err(_) => None
        }.expect("Invalid OggTrackBuf");

        let comments = match VorbisHeader::find_comments(ogg.pages()) {
            Ok(header) => header.comments(),
            Err(_) => None
        }.expect("Invalid OggTrackBuf");

        let mut sample_count = 0;
        for page in ogg.pages() {
            let page_pos = page.position();
            if sample_count < page_pos {
                sample_count = page_pos;
            }
        }

        let mut artist: Option<String> = None;
        let mut album: Option<String> = None;
        let mut title: Option<String> = None;

        for &(ref key, ref val) in comments.comments.iter() {
            if key.eq_ignore_ascii_case("ARTIST") {
                artist = Some(val.clone());
            }
            if key.eq_ignore_ascii_case("ALBUM") {
                album = Some(val.clone());
            }
            if key.eq_ignore_ascii_case("TITLE") {
                title = Some(val.clone());
            }
        }

        Track {
            handle: handle,
            data: ogg,
            comments: comments,

            artist: artist.unwrap_or_else(|| "".to_string()),
            album: album.unwrap_or_else(|| "".to_string()),
            title: title.unwrap_or_else(|| "".to_string()),

            sample_rate: id_header.audio_sample_rate as u64,
            sample_count: sample_count,
        }
    }

    pub fn into_inner(self) -> OggTrackBuf {
        self.data
    }

    pub fn get_track_info(&self) -> model::TrackInfo {
        model::TrackInfo {
            handle: self.handle,

            artist: self.artist.clone(),
            album: self.album.clone(),
            title: self.title.clone(),

            sample_rate: self.sample_rate,
            sample_count: self.sample_count,
            sample_position: 0,
        }
    }
}

pub struct PlayQueue {
    halloc: HandleAllocator<ChaChaRng>,
    items: VecDeque<Track>,
    history: VecDeque<model::TrackInfo>,
}

impl PlayQueue {
    pub fn new(limit: usize) -> PlayQueue {
        let rng: ChaChaRng = rand::thread_rng().gen();
        PlayQueue {
            halloc: HandleAllocator::new(rng, limit),
            items: VecDeque::new(),
            history: VecDeque::new(),
        }
    }

    #[allow(dead_code)] // queue manip stub
    pub fn reorder(&mut self, handle_ord: &[Handle]) {
        let old_items: VecDeque<Track> = mem::replace(&mut self.items, VecDeque::new());

        let mut map: HashMap<Handle, Track> = old_items.into_iter().map(|pq| (pq.handle, pq)).collect();

        for handle in handle_ord.iter() {
            match map.remove(handle) {
                Some(item) => {
                    self.items.push_back(item);
                },
                None => (),
            }
        }
    }

    #[allow(dead_code)] // queue manip stub
    pub fn remove_by_handle(&mut self, handle: Handle) {
        let old_items: VecDeque<Track> = mem::replace(&mut self.items, VecDeque::new());
        for item in old_items.into_iter() {
            if item.handle != handle {
                self.items.push_back(item);
            }
        }
    }

    pub fn add_track(&mut self, track: &OggTrack) -> Result<Handle, PlayQueueError> {
        let handle = try!(self.halloc.generate()
                .map_err(|()| PlayQueueError::Full));

        self.items.push_back(Track::from_ogg_track(handle, track.to_owned()));
        Ok(handle)
    }

    pub fn pop_track(&mut self) -> Option<Track> {
        match self.items.pop_front() {
            Some(track) => {
                self.add_history(track.get_track_info());
                Some(track)
            },
            None => None,
        }
    }

    pub fn track_infos(&self) -> Vec<model::TrackInfo> {
        self.items.iter()
            .map(Track::get_track_info)
            .collect()
    }

    fn add_history(&mut self, tinfo: model::TrackInfo) {
        self.history.push_back(tinfo);
        while 10 < self.history.len() {
            let track = self.history.pop_front().unwrap();
            self.halloc.dispose(track.handle).unwrap();
        }
    }

    pub fn get_history(&self) -> Vec<model::TrackInfo> {
        self.history.iter().skip(1).cloned().collect()
    }
}

pub enum PlayQueueError {
    Full,
}
<commit_msg>fix current song appearing in history<commit_after>use std::mem;
use std::collections::{VecDeque, HashMap, HashSet};

use rand::{self, Rng, ChaChaRng};

use ogg::{OggTrack, OggTrackBuf};
use ogg::vorbis::{Comments, VorbisHeader};
use ireul_interface::proxy::track::model::{self, Handle};

struct HandleAllocator<R> {
    rng: R,
    limit: usize,
    allocated: HashSet<u64>,
}

impl<R> HandleAllocator<R> where R: Rng {
    pub fn new(rng: R, limit: usize) -> HandleAllocator<R> {
        HandleAllocator {
            rng: rng,
            limit: limit,
            allocated: HashSet::new(),
        }
    }

    pub fn generate(&mut self) -> Result<Handle, ()> {
        if self.limit <= self.allocated.len() {
            return Err(());
        }

        let new_handle;
        loop {
            let foo = self.rng.next_u64();
            if !self.allocated.contains(&foo) {
                new_handle = foo;
                break;
            }
        }

        println!("EMITTING HANDLE {:#x}", new_handle);
        self.allocated.insert(new_handle);
        Ok(Handle(new_handle))
    }

    pub fn dispose(&mut self, handle: Handle) -> Result<(), Handle> {
        match self.allocated.remove(&handle.0) {
            true => Ok(()),
            false => Err(handle),
        }
    }
}

#[derive(Clone)]
pub struct Track {
    handle: Handle,
    data: OggTrackBuf,
    comments: Comments,

    artist: String,
    album: String,
    title: String,

    sample_rate: u64,
    sample_count: u64,
}

impl Track {
    pub fn from_ogg_track(handle: Handle, ogg: OggTrackBuf) -> Track {
        use std::ascii::AsciiExt;

        let id_header = match VorbisHeader::find_identification(ogg.pages()) {
            Ok(header) => header.identification_header(),
            Err(_) => None
        }.expect("Invalid OggTrackBuf");

        let comments = match VorbisHeader::find_comments(ogg.pages()) {
            Ok(header) => header.comments(),
            Err(_) => None
        }.expect("Invalid OggTrackBuf");

        let mut sample_count = 0;
        for page in ogg.pages() {
            let page_pos = page.position();
            if sample_count < page_pos {
                sample_count = page_pos;
            }
        }

        let mut artist: Option<String> = None;
        let mut album: Option<String> = None;
        let mut title: Option<String> = None;

        for &(ref key, ref val) in comments.comments.iter() {
            if key.eq_ignore_ascii_case("ARTIST") {
                artist = Some(val.clone());
            }
            if key.eq_ignore_ascii_case("ALBUM") {
                album = Some(val.clone());
            }
            if key.eq_ignore_ascii_case("TITLE") {
                title = Some(val.clone());
            }
        }

        Track {
            handle: handle,
            data: ogg,
            comments: comments,

            artist: artist.unwrap_or_else(|| "".to_string()),
            album: album.unwrap_or_else(|| "".to_string()),
            title: title.unwrap_or_else(|| "".to_string()),

            sample_rate: id_header.audio_sample_rate as u64,
            sample_count: sample_count,
        }
    }

    pub fn into_inner(self) -> OggTrackBuf {
        self.data
    }

    pub fn get_track_info(&self) -> model::TrackInfo {
        model::TrackInfo {
            handle: self.handle,

            artist: self.artist.clone(),
            album: self.album.clone(),
            title: self.title.clone(),

            sample_rate: self.sample_rate,
            sample_count: self.sample_count,
            sample_position: 0,
        }
    }
}

pub struct PlayQueue {
    halloc: HandleAllocator<ChaChaRng>,
    items: VecDeque<Track>,
    history: VecDeque<model::TrackInfo>,
}

impl PlayQueue {
    pub fn new(limit: usize) -> PlayQueue {
        let rng: ChaChaRng = rand::thread_rng().gen();
        PlayQueue {
            halloc: HandleAllocator::new(rng, limit),
            items: VecDeque::new(),
            history: VecDeque::new(),
        }
    }

    #[allow(dead_code)] // queue manip stub
    pub fn reorder(&mut self, handle_ord: &[Handle]) {
        let old_items: VecDeque<Track> = mem::replace(&mut self.items, VecDeque::new());

        let mut map: HashMap<Handle, Track> = old_items.into_iter().map(|pq| (pq.handle, pq)).collect();

        for handle in handle_ord.iter() {
            match map.remove(handle) {
                Some(item) => {
                    self.items.push_back(item);
                },
                None => (),
            }
        }
    }

    #[allow(dead_code)] // queue manip stub
    pub fn remove_by_handle(&mut self, handle: Handle) {
        let old_items: VecDeque<Track> = mem::replace(&mut self.items, VecDeque::new());
        for item in old_items.into_iter() {
            if item.handle != handle {
                self.items.push_back(item);
            }
        }
    }

    pub fn add_track(&mut self, track: &OggTrack) -> Result<Handle, PlayQueueError> {
        let handle = try!(self.halloc.generate()
                .map_err(|()| PlayQueueError::Full));

        self.items.push_back(Track::from_ogg_track(handle, track.to_owned()));
        Ok(handle)
    }

    pub fn pop_track(&mut self) -> Option<Track> {
        match self.items.pop_front() {
            Some(track) => {
                self.add_history(track.get_track_info());
                Some(track)
            },
            None => None,
        }
    }

    pub fn track_infos(&self) -> Vec<model::TrackInfo> {
        self.items.iter()
            .map(Track::get_track_info)
            .collect()
    }

    fn add_history(&mut self, tinfo: model::TrackInfo) {
        self.history.push_front(tinfo);
        while 10 < self.history.len() {
            let track = self.history.pop_back().unwrap();
            self.halloc.dispose(track.handle).unwrap();
        }
    }

    pub fn get_history(&self) -> Vec<model::TrackInfo> {
        self.history.iter().skip(1).cloned().collect()

    }
}

pub enum PlayQueueError {
    Full,
}
<|endoftext|>


<commit_before>//! Crate name parsing.
use semver;

use {get_crate_name_from_github, get_crate_name_from_gitlab, get_crate_name_from_path};
use Dependency;
use errors::*;

/// A crate specifier. This can be a plain name (e.g. `docopt`), a name and a versionreq (e.g.
/// `docopt@^0.8`), a URL, or a path.
#[derive(Debug)]
pub struct CrateName<'a>(&'a str);

impl<'a> CrateName<'a> {
    /// Create a new `CrateName`
    pub fn new(name: &'a str) -> Self {
        CrateName(name)
    }

    /// Does this specify a versionreq?
    pub fn has_version(&self) -> bool {
        self.0.contains('@')
    }

    /// Is this a URI?
    pub fn is_url_or_path(&self) -> bool {
        self.is_github_url() || self.is_gitlab_url() || self.is_path()
    }

    fn is_github_url(&self) -> bool {
        self.0.contains("https://github.com")
    }

    fn is_gitlab_url(&self) -> bool {
        self.0.contains("https://gitlab.com")
    }

    fn is_path(&self) -> bool {
        // FIXME: how else can we check if the name is a (possibly invalid) path?
        self.0.contains('.') || self.0.contains('/') || self.0.contains('\\')
    }

    /// If this crate specifier includes a version (e.g. `docopt@0.8`), extract the name and
    /// version.
    pub fn parse_as_version(&self) -> Result<Option<Dependency>> {
        if self.has_version() {
            let xs: Vec<_> = self.0.splitn(2, '@').collect();
            let (name, version) = (xs[0], xs[1]);
            semver::VersionReq::parse(version).chain_err(|| "Invalid crate version requirement")?;

            Ok(Some(Dependency::new(name).set_version(version)))
        // Ok(Some((name, version)))
        } else {
            Ok(None)
        }
    }

    /// Will parse this crate name on the assumption that it is a URI.
    pub fn parse_crate_name_from_uri(&self) -> Result<Dependency> {
        if self.is_github_url() {
            if let Ok(ref crate_name) = get_crate_name_from_github(self.0) {
                return Ok(Dependency::new(crate_name).set_git(self.0));
            }
        } else if self.is_gitlab_url() {
            if let Ok(ref crate_name) = get_crate_name_from_gitlab(self.0) {
                return Ok(Dependency::new(crate_name).set_git(self.0));
            }
        } else if self.is_path() {
            if let Ok(ref crate_name) = get_crate_name_from_path(self.0) {
                return Ok(Dependency::new(crate_name).set_path(self.0));
            }
        }

        bail!("Unable to obtain crate informations from `{}`.\n", self.0)
    }
}
<commit_msg>Remove old code<commit_after>//! Crate name parsing.
use semver;

use {get_crate_name_from_github, get_crate_name_from_gitlab, get_crate_name_from_path};
use Dependency;
use errors::*;

/// A crate specifier. This can be a plain name (e.g. `docopt`), a name and a versionreq (e.g.
/// `docopt@^0.8`), a URL, or a path.
#[derive(Debug)]
pub struct CrateName<'a>(&'a str);

impl<'a> CrateName<'a> {
    /// Create a new `CrateName`
    pub fn new(name: &'a str) -> Self {
        CrateName(name)
    }

    /// Does this specify a versionreq?
    pub fn has_version(&self) -> bool {
        self.0.contains('@')
    }

    /// Is this a URI?
    pub fn is_url_or_path(&self) -> bool {
        self.is_github_url() || self.is_gitlab_url() || self.is_path()
    }

    fn is_github_url(&self) -> bool {
        self.0.contains("https://github.com")
    }

    fn is_gitlab_url(&self) -> bool {
        self.0.contains("https://gitlab.com")
    }

    fn is_path(&self) -> bool {
        // FIXME: how else can we check if the name is a (possibly invalid) path?
        self.0.contains('.') || self.0.contains('/') || self.0.contains('\\')
    }

    /// If this crate specifier includes a version (e.g. `docopt@0.8`), extract the name and
    /// version.
    pub fn parse_as_version(&self) -> Result<Option<Dependency>> {
        if self.has_version() {
            let xs: Vec<_> = self.0.splitn(2, '@').collect();
            let (name, version) = (xs[0], xs[1]);
            semver::VersionReq::parse(version).chain_err(|| "Invalid crate version requirement")?;

            Ok(Some(Dependency::new(name).set_version(version)))
        } else {
            Ok(None)
        }
    }

    /// Will parse this crate name on the assumption that it is a URI.
    pub fn parse_crate_name_from_uri(&self) -> Result<Dependency> {
        if self.is_github_url() {
            if let Ok(ref crate_name) = get_crate_name_from_github(self.0) {
                return Ok(Dependency::new(crate_name).set_git(self.0));
            }
        } else if self.is_gitlab_url() {
            if let Ok(ref crate_name) = get_crate_name_from_gitlab(self.0) {
                return Ok(Dependency::new(crate_name).set_git(self.0));
            }
        } else if self.is_path() {
            if let Ok(ref crate_name) = get_crate_name_from_path(self.0) {
                return Ok(Dependency::new(crate_name).set_path(self.0));
            }
        }

        bail!("Unable to obtain crate informations from `{}`.\n", self.0)
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate linefeed;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

#[macro_use]
mod utils;
mod application_context;
mod command_executor;
#[macro_use]
mod commands;
mod indy_context;
mod libindy;

use application_context::ApplicationContext;
use command_executor::CommandExecutor;

use commands::{common, did, pool, wallet, ledger};
use indy_context::IndyContext;

use linefeed::{Reader, ReadResult};
use linefeed::complete::PathCompleter;

use std::env;
use std::rc::Rc;

fn main() {
    utils::logger::LoggerUtils::init();

    let application_context = Rc::new(ApplicationContext::new());
    let indy_context = Rc::new(IndyContext::new());

    let command_executor = build_executor(application_context.clone(), indy_context);

    if env::args().len() == 1 {
        execute_interactive(command_executor, application_context);
    } else {
        execute_batch(command_executor, &env::args().next().unwrap())
    }
}

fn build_executor(application_context: Rc<ApplicationContext>,
                  indy_context: Rc<IndyContext>) -> CommandExecutor {
    CommandExecutor::build()
        .add_command(Box::new(common::AboutCommand::new()))
        .add_command(Box::new(common::ExitCommand::new(application_context.clone())))
        .add_command(Box::new(common::PromptCommand::new(application_context.clone())))
        .add_command(Box::new(common::ShowCommand::new()))
        .add_group(Box::new(did::Group::new()))
        .add_command(Box::new(did::NewCommand::new(indy_context.clone())))
        .add_command(Box::new(did::UseCommand::new(indy_context.clone())))
        .add_command(Box::new(did::RotateKeyCommand::new(indy_context.clone())))
        .add_command(Box::new(did::ListCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(pool::Group::new()))
        .add_command(Box::new(pool::CreateCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::ConnectCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::ListCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::DisconnectCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::DeleteCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(wallet::Group::new()))
        .add_command(Box::new(wallet::CreateCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::OpenCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::ListCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::CloseCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::DeleteCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(ledger::Group::new()))
        .add_command(Box::new(ledger::SendNymCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetNymCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendAttribCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetAttribCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendSchemaCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetSchemaCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendClaimDefCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetClaimDefCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendNodeCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SenCustomCommand::new(indy_context.clone())))
        .finalize_group()
        .finalize()
}

fn execute_interactive(command_executor: CommandExecutor,
                       application_context: Rc<ApplicationContext>) {
    #[cfg(target_os = "windows")]
        ansi_term::enable_ansi_support().is_ok();

    let mut reader = Reader::new("indy-cli").unwrap();
    reader.set_completer(Rc::new(PathCompleter));
    reader.set_prompt(&application_context.get_prompt());

    while let Ok(ReadResult::Input(line)) = reader.read_line() {
        if line.trim().is_empty() {
            continue;
        }

        command_executor.execute(&line).is_ok();
        reader.add_history(line);
        reader.set_prompt(&application_context.get_prompt());

        if application_context.is_exit() {
            break;
        }
    }
}

fn execute_batch(_command_executor: CommandExecutor, _script_path: &str) {
    unimplemented!()
}
<commit_msg>[cli] Implement batch mode.<commit_after>extern crate ansi_term;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate linefeed;
#[macro_use]
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

#[macro_use]
mod utils;
mod application_context;
mod command_executor;
#[macro_use]
mod commands;
mod indy_context;
mod libindy;

use application_context::ApplicationContext;
use command_executor::CommandExecutor;

use commands::{common, did, pool, wallet, ledger};
use indy_context::IndyContext;

use linefeed::{Reader, ReadResult};
use linefeed::complete::PathCompleter;

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::rc::Rc;

fn main() {
    utils::logger::LoggerUtils::init();

    let application_context = Rc::new(ApplicationContext::new());
    let indy_context = Rc::new(IndyContext::new());

    let command_executor = build_executor(application_context.clone(), indy_context);

    if env::args().len() == 1 {
        execute_interactive(command_executor, application_context);
    } else {
        let mut args = env::args();
        args.next(); //skip 0 param
        execute_batch(command_executor, &args.next().unwrap())
    }
}

fn build_executor(application_context: Rc<ApplicationContext>,
                  indy_context: Rc<IndyContext>) -> CommandExecutor {
    CommandExecutor::build()
        .add_command(Box::new(common::AboutCommand::new()))
        .add_command(Box::new(common::ExitCommand::new(application_context.clone())))
        .add_command(Box::new(common::PromptCommand::new(application_context.clone())))
        .add_command(Box::new(common::ShowCommand::new()))
        .add_group(Box::new(did::Group::new()))
        .add_command(Box::new(did::NewCommand::new(indy_context.clone())))
        .add_command(Box::new(did::UseCommand::new(indy_context.clone())))
        .add_command(Box::new(did::RotateKeyCommand::new(indy_context.clone())))
        .add_command(Box::new(did::ListCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(pool::Group::new()))
        .add_command(Box::new(pool::CreateCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::ConnectCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::ListCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::DisconnectCommand::new(indy_context.clone())))
        .add_command(Box::new(pool::DeleteCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(wallet::Group::new()))
        .add_command(Box::new(wallet::CreateCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::OpenCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::ListCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::CloseCommand::new(indy_context.clone())))
        .add_command(Box::new(wallet::DeleteCommand::new(indy_context.clone())))
        .finalize_group()
        .add_group(Box::new(ledger::Group::new()))
        .add_command(Box::new(ledger::SendNymCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetNymCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendAttribCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetAttribCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendSchemaCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetSchemaCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendClaimDefCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::GetClaimDefCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SendNodeCommand::new(indy_context.clone())))
        .add_command(Box::new(ledger::SenCustomCommand::new(indy_context.clone())))
        .finalize_group()
        .finalize()
}

fn execute_interactive(command_executor: CommandExecutor,
                       application_context: Rc<ApplicationContext>) {
    #[cfg(target_os = "windows")]
        ansi_term::enable_ansi_support().is_ok();

    let mut reader = Reader::new("indy-cli").unwrap();
    reader.set_completer(Rc::new(PathCompleter));
    reader.set_prompt(&application_context.get_prompt());

    while let Ok(ReadResult::Input(line)) = reader.read_line() {
        if line.trim().is_empty() {
            continue;
        }

        command_executor.execute(&line).is_ok();
        reader.add_history(line);
        reader.set_prompt(&application_context.get_prompt());

        if application_context.is_exit() {
            break;
        }
    }
}

fn execute_batch(command_executor: CommandExecutor, script_path: &str) {
    let file = match File::open(script_path) {
        Ok(file) => file,
        Err(err) => return println_err!("Can't open script file {}\nError: {}", script_path, err),
    };
    let reader = BufReader::new(file);

    let mut line_nym = 1;
    for line in reader.lines() {
        let line = if let Ok(line) = line { line } else {
            return println_err!("Can't parse line #{}", line_nym);
        };
        println!("{}", line);
        let (line, force) = if line.starts_with("-") {
            (line[1..].as_ref(), true)
        } else {
            (line[0..].as_ref(), false)
        };
        if command_executor.execute(line).is_err() && !force {
            return println_err!("Batch execution failed at line #{}", line_nym);
        }
        println!();
        line_nym += 1;
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate docopt;
#[macro_use]
extern crate log;
#[macro_use]
extern crate rocket;
extern crate rowdy;
extern crate rustc_serialize;
extern crate serde;
extern crate serde_json;

use docopt::Docopt;
use rowdy::auth;
use rocket::Rocket;

const USAGE: &'static str = r#"
rowdy JSON Web Token Authentication Server

Usage:
  rowdy noop <configuration-json>
  rowdy csv <configuration-json>
  rowdy (-h | --help)

Provide a configuration JSON file to run `rowdy` with. For available fields and examples for the JSON
configuration, refer to the documentation at https://lawliet89.github.io/rowdy/rowdy/struct.Configuration.html

You can also, additionally, configure Rocket by using `Rocket.toml` file.
See https://rocket.rs/guide/overview#configuration

The `noop` subcommand allows all username and passwords to authenticate.
The `csv` subcommand uses a CSV file as its username database. See
https://lawliet89.github.io/rowdy/rowdy/auth/simple/index.html for the database format.

The subcommands will change the format expected by the `basic_authenticator` key of the configuration JSON.
  - noop: The key is expected to be simply an empty map: i.e. `{}`
  - csv: The key should behave according to the format documented at
    https://lawliet89.github.io/rowdy/rowdy/auth/struct.SimpleAuthenticatorConfiguration.html
  - ldap: The key should behave according to the format documented at
    https://lawliet89.github.io/rowdy/rowdy/auth/struct.LdapAuthenticator.html

Options:
  -h --help                 Show this screen.
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_configuration_json: String,
    cmd_noop: bool,
    cmd_csv: bool,
    cmd_ldap: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let rocket = if args.cmd_noop {
        ignite::<auth::NoOpConfiguration>(&args.arg_configuration_json)
    } else if args.cmd_csv {
        ignite::<auth::SimpleAuthenticatorConfiguration>(&args.arg_configuration_json)
    } else if args.cmd_ldap {
        ignite::<auth::LdapAuthenticator>(&args.arg_configuration_json)
    } else {
        unreachable!("Should never happen");
    };

    let rocket = rocket.unwrap_or_else(|e| panic!("{}", e));
    rocket.mount("/", rowdy::routes()).launch()
}

/// Read configuration files, and ignite a `Rocket`
fn ignite<B>(path: &str) -> Result<Rocket, rowdy::Error>
    where B: auth::AuthenticatorConfiguration<auth::Basic>
{
    let config = read_config::<B>(path)?;
    config.ignite()
}

fn read_config<B>(path: &str) -> Result<rowdy::Configuration<B>, String>
    where B: auth::AuthenticatorConfiguration<auth::Basic>
{
    use std::fs::File;
    use std::io::Read;

    info_!("Reading configuration from '{}'", path);
    let mut file = File::open(&path).map_err(|e| format!("{:?}", e))?;
    let mut config_json = String::new();
    file.read_to_string(&mut config_json).map_err(|e| format!("{:?}", e))?;

    deserialize_json(&config_json)
}

pub fn deserialize_json<T>(json: &str) -> Result<T, String>
    where T: serde::Deserialize
{
    serde_json::from_str(json).map_err(|e| format!("{:?}", e))
}

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

    #[test]
    fn ignite_noop() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_noop.json").unwrap();
    }

    #[test]
    fn ignite_csv() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_csv.json").unwrap();
    }

    #[test]
    fn ignite_ldap() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_ldap.json").unwrap();
    }
}
<commit_msg>Fix docopt<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate docopt;
#[macro_use]
extern crate log;
#[macro_use]
extern crate rocket;
extern crate rowdy;
extern crate rustc_serialize;
extern crate serde;
extern crate serde_json;

use docopt::Docopt;
use rowdy::auth;
use rocket::Rocket;

const USAGE: &'static str = r#"
rowdy JSON Web Token Authentication Server

Usage:
  rowdy noop <configuration-json>
  rowdy csv <configuration-json>
  rowdy ldap <configuration-json>
  rowdy (-h | --help)

Provide a configuration JSON file to run `rowdy` with. For available fields and examples for the JSON
configuration, refer to the documentation at https://lawliet89.github.io/rowdy/rowdy/struct.Configuration.html

You can also, additionally, configure Rocket by using `Rocket.toml` file.
See https://rocket.rs/guide/overview#configuration

The `noop` subcommand allows all username and passwords to authenticate.
The `csv` subcommand uses a CSV file as its username database. See
https://lawliet89.github.io/rowdy/rowdy/auth/simple/index.html for the database format.

The subcommands will change the format expected by the `basic_authenticator` key of the configuration JSON.
  - noop: The key is expected to be simply an empty map: i.e. `{}`
  - csv: The key should behave according to the format documented at
    https://lawliet89.github.io/rowdy/rowdy/auth/struct.SimpleAuthenticatorConfiguration.html
  - ldap: The key should behave according to the format documented at
    https://lawliet89.github.io/rowdy/rowdy/auth/struct.LdapAuthenticator.html

Options:
  -h --help                 Show this screen.
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_configuration_json: String,
    cmd_noop: bool,
    cmd_csv: bool,
    cmd_ldap: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    let rocket = if args.cmd_noop {
        ignite::<auth::NoOpConfiguration>(&args.arg_configuration_json)
    } else if args.cmd_csv {
        ignite::<auth::SimpleAuthenticatorConfiguration>(&args.arg_configuration_json)
    } else if args.cmd_ldap {
        ignite::<auth::LdapAuthenticator>(&args.arg_configuration_json)
    } else {
        unreachable!("Should never happen");
    };

    let rocket = rocket.unwrap_or_else(|e| panic!("{}", e));
    rocket.mount("/", rowdy::routes()).launch()
}

/// Read configuration files, and ignite a `Rocket`
fn ignite<B>(path: &str) -> Result<Rocket, rowdy::Error>
    where B: auth::AuthenticatorConfiguration<auth::Basic>
{
    let config = read_config::<B>(path)?;
    config.ignite()
}

fn read_config<B>(path: &str) -> Result<rowdy::Configuration<B>, String>
    where B: auth::AuthenticatorConfiguration<auth::Basic>
{
    use std::fs::File;
    use std::io::Read;

    info_!("Reading configuration from '{}'", path);
    let mut file = File::open(&path).map_err(|e| format!("{:?}", e))?;
    let mut config_json = String::new();
    file.read_to_string(&mut config_json).map_err(|e| format!("{:?}", e))?;

    deserialize_json(&config_json)
}

pub fn deserialize_json<T>(json: &str) -> Result<T, String>
    where T: serde::Deserialize
{
    serde_json::from_str(json).map_err(|e| format!("{:?}", e))
}

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

    #[test]
    fn ignite_noop() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_noop.json").unwrap();
    }

    #[test]
    fn ignite_csv() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_csv.json").unwrap();
    }

    #[test]
    fn ignite_ldap() {
        ignite::<auth::NoOpConfiguration>("test/fixtures/config_ldap.json").unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(test, feature(test))]
#[cfg(test)]
extern crate test;

extern crate rand;
extern crate slog;
extern crate time;

extern crate tikv;

mod kv_generator;
mod logging;
mod macros;
mod security;

use std::env;

pub use kv_generator::*;
pub use logging::*;
pub use macros::*;
pub use security::*;

pub fn setup_for_ci() {
    if env::var("CI").is_ok() && env::var("LOG_FILE").is_ok() {
        logging::init_log_for_test();
    }
    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        tikv::util::set_panic_hook(true, "./");
    }

    // HACK! Always use epollex in tests.
    // See more: https://github.com/grpc/grpc/blob/v1.17.2/src/core/lib/iomgr/ev_posix.cc#L124
    env::set_var("GRPC_POLL_STRATEGY", "epollex");

    tikv::util::check_environment_variables();
}
<commit_msg>test: fix epollex error on macOS (#4084)<commit_after>// Copyright 2018 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(test, feature(test))]
#[cfg(test)]
extern crate test;

extern crate rand;
extern crate slog;
extern crate time;

extern crate tikv;

mod kv_generator;
mod logging;
mod macros;
mod security;

use std::env;

pub use kv_generator::*;
pub use logging::*;
pub use macros::*;
pub use security::*;

pub fn setup_for_ci() {
    if env::var("CI").is_ok() && env::var("LOG_FILE").is_ok() {
        logging::init_log_for_test();
    }
    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        tikv::util::set_panic_hook(true, "./");
    }

    // HACK! Always use epollex in tests on Linux.
    // See more: https://github.com/grpc/grpc/blob/v1.17.2/src/core/lib/iomgr/ev_posix.cc#L124
    #[cfg(target_os = "linux")]
    env::set_var("GRPC_POLL_STRATEGY", "epollex");

    tikv::util::check_environment_variables();
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	let is_docs_rs = env::var_os("DOCS_RS").is_some();
	if !targets_hermit || runs_clippy || is_docs_rs {
		return;
	}

	let kernel_src = KernelSrc::local().unwrap_or_else(KernelSrc::download);

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn local() -> Option<Self> {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		src_dir.exists().then_some(Self { src_dir })
	}

	fn download() -> Self {
		let version = "0.5.0";
		let out_dir = out_dir();
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = target_dir();
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");

		let mut cmd = Command::new("cargo");

		// Remove rust-toolchain-specific environment variables from kernel cargo
		cmd.env_remove("LD_LIBRARY_PATH");
		env::vars()
			.filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST"))
			.for_each(|(key, _value)| {
				cmd.env_remove(&key);
			});

		cmd.current_dir(&self.src_dir)
			.arg("run")
			.arg("--package=xtask")
			.arg("--target-dir")
			.arg(&target_dir)
			.arg("--")
			.arg("build")
			.arg("--arch")
			.arg(&arch)
			.args(&[
				"--profile",
				match profile.as_str() {
					"debug" => "dev",
					profile => profile,
				},
			])
			.arg("--target-dir")
			.arg(&target_dir);

		if has_feature("instrument") {
			cmd.arg("--instrument-mcount");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			[
				"acpi", "dhcpv4", "fsgsbase", "pci", "pci-ids", "smp", "tcp", "vga",
			]
			.into_iter(),
		);

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(&arch)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn out_dir() -> PathBuf {
	env::var_os("OUT_DIR").unwrap().into()
}

fn target_dir() -> PathBuf {
	let mut target_dir = out_dir();
	target_dir.push("target");
	target_dir
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.args(&["--features", &features.join(" ")]);
	}
}
<commit_msg>Fix clippy::needless-borrow<commit_after>use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;

use flate2::read::GzDecoder;
use tar::Archive;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	let runs_clippy =
		matches!(env::var_os("CARGO_CFG_FEATURE"), Some(os) if os == OsStr::new("cargo-clippy"));
	let is_docs_rs = env::var_os("DOCS_RS").is_some();
	if !targets_hermit || runs_clippy || is_docs_rs {
		return;
	}

	let kernel_src = KernelSrc::local().unwrap_or_else(KernelSrc::download);

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn local() -> Option<Self> {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		src_dir.exists().then_some(Self { src_dir })
	}

	fn download() -> Self {
		let version = "0.5.0";
		let out_dir = out_dir();
		let src_dir = out_dir.join(format!("libhermit-rs-{version}"));

		if !src_dir.exists() {
			let url = format!(
				"https://github.com/hermitcore/libhermit-rs/archive/refs/tags/v{version}.tar.gz"
			);
			let response = ureq::get(url.as_str()).call().unwrap().into_reader();
			let tar = GzDecoder::new(response);
			let mut archive = Archive::new(tar);
			archive.unpack(src_dir.parent().unwrap()).unwrap();
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = target_dir();
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");

		let mut cmd = Command::new("cargo");

		// Remove rust-toolchain-specific environment variables from kernel cargo
		cmd.env_remove("LD_LIBRARY_PATH");
		env::vars()
			.filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST"))
			.for_each(|(key, _value)| {
				cmd.env_remove(&key);
			});

		cmd.current_dir(&self.src_dir)
			.arg("run")
			.arg("--package=xtask")
			.arg("--target-dir")
			.arg(&target_dir)
			.arg("--")
			.arg("build")
			.arg("--arch")
			.arg(&arch)
			.args([
				"--profile",
				match profile.as_str() {
					"debug" => "dev",
					profile => profile,
				},
			])
			.arg("--target-dir")
			.arg(&target_dir);

		if has_feature("instrument") {
			cmd.arg("--instrument-mcount");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			[
				"acpi", "dhcpv4", "fsgsbase", "pci", "pci-ids", "smp", "tcp", "vga",
			]
			.into_iter(),
		);

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(&arch)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn out_dir() -> PathBuf {
	env::var_os("OUT_DIR").unwrap().into()
}

fn target_dir() -> PathBuf {
	let mut target_dir = out_dir();
	target_dir.push("target");
	target_dir
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.args(["--features", &features.join(" ")]);
	}
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! forward_features {
	($cmd:expr, $($feature:literal,)+ ) => {
		let mut features = vec![];

		$(
			if cfg!(feature = $feature) {
				features.push($feature);
			}
		)+

		if !features.is_empty() {
			$cmd.arg("--features");
			$cmd.arg(features.join(" "));
		}
	};
}

fn build_hermit(src_dir: &Path) {
	let target_dir = {
		let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		target_dir.push("target");
		target_dir
	};
	let manifest_path = src_dir.join("Cargo.toml");
	assert!(
		manifest_path.exists(),
		"kernel manifest path `{}` does not exist",
		manifest_path.display()
	);
	let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	let kernel_triple = match target_arch.as_str() {
		"x86_64" => "x86_64-unknown-none-hermitkernel",
		"aarch64" => "aarch64-unknown-hermit",
		_ => panic!("Unsupported target arch: {}", target_arch),
	};

	cmd.arg("build")
		.arg("-Z")
		.arg("build-std=core,alloc")
		.arg("--target")
		.arg(kernel_triple)
		.arg("--manifest-path")
		.arg("Cargo.toml");

	cmd.current_dir(src_dir);

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	cmd.arg("--target-dir").arg(&target_dir);

	if profile == "release" {
		cmd.arg("--release");
	}

	// Control enabled features via this crate's features
	cmd.arg("--no-default-features");
	forward_features!(cmd, "acpi", "fsgsbase", "pci", "smp", "vga",);

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = target_dir
		.join(kernel_triple)
		.join(&profile)
		.canonicalize()
		.unwrap();

	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

	if target_arch == "aarch64" {
		symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
	}

	rename_symbols(symbols, &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	println!("cargo:rerun-if-changed={}", src_dir.display());
	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbols(symbols: impl IntoIterator<Item = impl AsRef<OsStr>>, lib: impl AsRef<Path>) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref());
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>Sys: Remove rustc-dep-of-std<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! forward_features {
	($cmd:expr, $($feature:literal,)+ ) => {
		let mut features = vec![];

		$(
			if cfg!(feature = $feature) {
				features.push($feature);
			}
		)+

		if !features.is_empty() {
			$cmd.arg("--features");
			$cmd.arg(features.join(" "));
		}
	};
}

fn build_hermit(src_dir: &Path) {
	let target_dir = {
		let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		target_dir.push("target");
		target_dir
	};
	let manifest_path = src_dir.join("Cargo.toml");
	assert!(
		manifest_path.exists(),
		"kernel manifest path `{}` does not exist",
		manifest_path.display()
	);
	let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	let kernel_triple = match target_arch.as_str() {
		"x86_64" => "x86_64-unknown-none-hermitkernel",
		"aarch64" => "aarch64-unknown-hermit",
		_ => panic!("Unsupported target arch: {}", target_arch),
	};

	cmd.arg("build")
		.arg("-Z")
		.arg("build-std=core,alloc")
		.arg("--target")
		.arg(kernel_triple)
		.arg("--manifest-path")
		.arg("Cargo.toml");

	cmd.current_dir(src_dir);

	cmd.env_remove("RUSTUP_TOOLCHAIN");
	if option_env!("RUSTC_WORKSPACE_WRAPPER")
		.unwrap_or_default()
		.ends_with("clippy-driver")
	{
		cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
	}

	cmd.env("CARGO_TERM_COLOR", "always");

	cmd.arg("--target-dir").arg(&target_dir);

	if profile == "release" {
		cmd.arg("--release");
	}

	// Control enabled features via this crate's features
	cmd.arg("--no-default-features");
	forward_features!(cmd, "acpi", "fsgsbase", "pci", "smp", "vga",);

	let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
	let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

	#[cfg(feature = "instrument")]
	{
		rustflags.push("-Zinstrument-mcount".to_string());
		// Add outer rustflags to command
		rustflags.push(outer_rustflags);
	}

	#[cfg(not(feature = "instrument"))]
	{
		// If the `instrument` feature feature is not enabled,
		// filter it from outer rustflags before adding them to the command.
		if !outer_rustflags.is_empty() {
			let flags = outer_rustflags
				.split('\x1f')
				.filter(|&flag| !flag.contains("instrument-mcount"))
				.map(String::from);
			rustflags.extend(flags);
		}
	}

	cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

	let status = cmd.status().expect("failed to start kernel build");
	assert!(status.success());

	let lib_location = target_dir
		.join(kernel_triple)
		.join(&profile)
		.canonicalize()
		.unwrap();

	println!("Lib location: {}", lib_location.display());

	let lib = lib_location.join("libhermit.a");

	let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

	if target_arch == "aarch64" {
		symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
	}

	rename_symbols(symbols, &lib);

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	println!("cargo:rerun-if-changed={}", src_dir.display());
	// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

/// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
/// Consequently, we rename the functions in the libos to avoid collisions.
fn rename_symbols(symbols: impl IntoIterator<Item = impl AsRef<OsStr>>, lib: impl AsRef<Path>) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib.as_ref())
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}

#[cfg(not(feature = "with_submodule"))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	if !src_dir.as_path().exists() {
		let status = Command::new(env!("CARGO"))
			.current_dir(out_dir)
			.arg("download")
			.arg("--output")
			.arg(src_dir.clone().into_os_string())
			.arg("--extract")
			.arg("rusty-hermit")
			.status()
			.expect("failed to start kernel download");
		assert!(
			status.success(),
			"Unable to download rusty-hermit. Is cargo-download installed?"
		);
	}

	build_hermit(src_dir.as_ref());
}

#[cfg(feature = "with_submodule")]
fn build() {
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref());
}

fn main() {
	build();
}
<|endoftext|>