<commit_before>use crate::hcore::package::PackageIdent;
use hyper::status::StatusCode;
use std::str::FromStr;

use crate::{api_client,
            common::ui::{Status,
                         UIReader,
                         UIWriter,
                         UI},
            hcore::ChannelIdent};

use crate::{error::{Error,
                    Result},
            PRODUCT,
            VERSION};

fn is_ident(s: &str) -> bool { PackageIdent::from_str(s).is_ok() }

fn in_origin(ident: &str, origin: Option<&str>) -> bool {
    if origin.is_some() {
        let pi = PackageIdent::from_str(ident).unwrap(); // unwrap Ok
        origin.unwrap() == pi.origin
    } else {
        true
    }
}

pub fn get_ident_list(ui: &mut UI,
                      group_status: &api_client::SchedulerResponse,
                      origin: Option<&str>,
                      interactive: bool)
                      -> Result<Vec<String>> {
    let mut idents: Vec<String> =
        group_status.projects
                    .iter()
                    .cloned()
                    .filter(|p| p.state == "Success" && in_origin(&p.ident, origin))
                    .map(|p| p.ident)
                    .collect();

    if idents.is_empty() || !interactive {
        return Ok(idents);
    }

    let prelude = "# This is the list of package identifiers that will be processed.\n# You may \
                   edit this file and remove any packages that you do\n# not want to apply to the \
                   specified channel.\n"
                                        .to_string();

    idents.insert(0, prelude);

    idents = idents.iter().map(|s| format!("{}\n", s)).collect();

    Ok(ui.edit(&idents)?
         .split('\n')
         .filter(|s| is_ident(s))
         .map(str::to_string)
         .collect())
}

fn get_group_status(bldr_url: &str, group_id: u64) -> Result<api_client::SchedulerResponse> {
    let api_client =
        api_client::Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;

    let group_status = api_client.get_schedule(group_id as i64, true)
                                 .map_err(Error::ScheduleStatus)?;

    Ok(group_status)
}

#[allow(clippy::too_many_arguments)]
pub fn start(ui: &mut UI,
             bldr_url: &str,
             group_id: &str,
             channel: &ChannelIdent,
             origin: Option<&str>,
             interactive: bool,
             verbose: bool,
             token: &str,
             promote: bool)
             -> Result<()> {
    let api_client =
        api_client::Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;
    let (promoted_demoted, promoting_demoting, to_from, changing_status, changed_status) =
        if promote {
            ("promoted", "Promoting", "to", Status::Promoting, Status::Promoted)
        } else {
            ("demoted", "Demoting", "from", Status::Demoting, Status::Demoted)
        };

    let gid = match group_id.parse::<u64>() {
        Ok(g) => g,
        Err(e) => {
            ui.fatal(format!("Failed to parse group id: {}", e))?;
            return Err(Error::ParseIntError(e));
        }
    };

    let group_status = get_group_status(bldr_url, gid)?;
    let idents = get_ident_list(ui, &group_status, origin, interactive)?;

    if idents.is_empty() {
        ui.warn("No matching packages found")?;
        return Ok(());
    }

    if verbose {
        println!("Packages being {}:", promoted_demoted);
        for ident in idents.iter() {
            println!("  {}", ident)
        }
    }

    let question = format!("{} {} package(s) to channel '{}'. Continue?",
                           promoting_demoting,
                           idents.len(),
                           channel);

    if !ui.prompt_yes_no(&question, Some(true))? {
        ui.fatal("Aborted")?;
        return Ok(());
    }

    ui.status(changing_status,
              format!("job group {} {} channel '{}'", group_id, to_from, channel))?;

    match api_client.job_group_promote_or_demote(gid, &idents, channel, token, promote) {
        Ok(_) => {
            ui.status(changed_status,
                      format!("job group {} {} channel '{}'", group_id, to_from, channel))?;
        }
        Err(api_client::Error::APIError(StatusCode::UnprocessableEntity, _)) => {
            return Err(Error::JobGroupPromoteOrDemoteUnprocessable(promote));
        }
        Err(e) => {
            return Err(Error::JobGroupPromoteOrDemote(e, promote));
        }
    };

    Ok(())
}

#[cfg(test)]
mod test {
    use std::{env,
              io::{self,
                   Cursor,
                   Write},
              sync::{Arc,
                     RwLock}};
    use termcolor::ColorChoice;

    use super::get_ident_list;
    use crate::{api_client::{Project,
                             SchedulerResponse},
                common::ui::UI};

    fn sample_project_list() -> Vec<Project> {
        let project1 = Project { name:   "Project1".to_string(),
                                 ident:  "core/project1/1.0.0/20180101000000".to_string(),
                                 state:  "Success".to_string(),
                                 job_id: "12345678".to_string(),
                                 target: "x86_64-linux".to_string(), };
        let project2 = Project { name:   "Project2".to_string(),
                                 ident:  "core/project2/1.0.0/20180101000000".to_string(),
                                 state:  "Success".to_string(),
                                 job_id: "12345678".to_string(),
                                 target: "x86_64-linux".to_string(), };

        vec![project1, project2]
    }

    fn ui() -> (UI, OutputBuffer, OutputBuffer) {
        let stdout_buf = OutputBuffer::new();
        let stderr_buf = OutputBuffer::new();

        let ui = UI::with_streams(Box::new(io::empty()),
                                  || Box::new(stdout_buf.clone()),
                                  || Box::new(stderr_buf.clone()),
                                  ColorChoice::Never,
                                  false);

        (ui, stdout_buf, stderr_buf)
    }

    #[derive(Clone)]
    pub struct OutputBuffer {
        pub cursor: Arc<RwLock<Cursor<Vec<u8>>>>,
    }

    impl OutputBuffer {
        fn new() -> Self {
            OutputBuffer { cursor: Arc::new(RwLock::new(Cursor::new(Vec::new()))), }
        }
    }

    impl Write for OutputBuffer {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.cursor
                .write()
                .expect("Cursor lock is poisoned")
                .write(buf)
        }

        fn flush(&mut self) -> io::Result<()> {
            self.cursor
                .write()
                .expect("Cursor lock is poisoned")
                .flush()
        }
    }

    #[test]
    fn test_get_ident_list() {
        let (mut ui, _stdout, _stderr) = ui();
        let group_status = SchedulerResponse { id:           "12345678".to_string(),
                                               state:        "Finished".to_string(),
                                               projects:     sample_project_list(),
                                               created_at:
                                                   "Properly formated timestamp".to_string(),
                                               project_name: "Test Project".to_string(),
                                               target:       "x86_64-linux".to_string(), };

        let ident_list =
            get_ident_list(&mut ui, &group_status, Some("core"), false).expect("Error fetching \
                                                                                ident list");

        assert_eq!(ident_list,
                   ["core/project1/1.0.0/20180101000000",
                    "core/project2/1.0.0/20180101000000",])
    }
}
<commit_msg>Remove superfluous use statement<commit_after>use crate::hcore::package::PackageIdent;
use hyper::status::StatusCode;
use std::str::FromStr;

use crate::{api_client,
            common::ui::{Status,
                         UIReader,
                         UIWriter,
                         UI},
            hcore::ChannelIdent};

use crate::{error::{Error,
                    Result},
            PRODUCT,
            VERSION};

fn is_ident(s: &str) -> bool { PackageIdent::from_str(s).is_ok() }

fn in_origin(ident: &str, origin: Option<&str>) -> bool {
    if origin.is_some() {
        let pi = PackageIdent::from_str(ident).unwrap(); // unwrap Ok
        origin.unwrap() == pi.origin
    } else {
        true
    }
}

pub fn get_ident_list(ui: &mut UI,
                      group_status: &api_client::SchedulerResponse,
                      origin: Option<&str>,
                      interactive: bool)
                      -> Result<Vec<String>> {
    let mut idents: Vec<String> =
        group_status.projects
                    .iter()
                    .cloned()
                    .filter(|p| p.state == "Success" && in_origin(&p.ident, origin))
                    .map(|p| p.ident)
                    .collect();

    if idents.is_empty() || !interactive {
        return Ok(idents);
    }

    let prelude = "# This is the list of package identifiers that will be processed.\n# You may \
                   edit this file and remove any packages that you do\n# not want to apply to the \
                   specified channel.\n"
                                        .to_string();

    idents.insert(0, prelude);

    idents = idents.iter().map(|s| format!("{}\n", s)).collect();

    Ok(ui.edit(&idents)?
         .split('\n')
         .filter(|s| is_ident(s))
         .map(str::to_string)
         .collect())
}

fn get_group_status(bldr_url: &str, group_id: u64) -> Result<api_client::SchedulerResponse> {
    let api_client =
        api_client::Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;

    let group_status = api_client.get_schedule(group_id as i64, true)
                                 .map_err(Error::ScheduleStatus)?;

    Ok(group_status)
}

#[allow(clippy::too_many_arguments)]
pub fn start(ui: &mut UI,
             bldr_url: &str,
             group_id: &str,
             channel: &ChannelIdent,
             origin: Option<&str>,
             interactive: bool,
             verbose: bool,
             token: &str,
             promote: bool)
             -> Result<()> {
    let api_client =
        api_client::Client::new(bldr_url, PRODUCT, VERSION, None).map_err(Error::APIClient)?;
    let (promoted_demoted, promoting_demoting, to_from, changing_status, changed_status) =
        if promote {
            ("promoted", "Promoting", "to", Status::Promoting, Status::Promoted)
        } else {
            ("demoted", "Demoting", "from", Status::Demoting, Status::Demoted)
        };

    let gid = match group_id.parse::<u64>() {
        Ok(g) => g,
        Err(e) => {
            ui.fatal(format!("Failed to parse group id: {}", e))?;
            return Err(Error::ParseIntError(e));
        }
    };

    let group_status = get_group_status(bldr_url, gid)?;
    let idents = get_ident_list(ui, &group_status, origin, interactive)?;

    if idents.is_empty() {
        ui.warn("No matching packages found")?;
        return Ok(());
    }

    if verbose {
        println!("Packages being {}:", promoted_demoted);
        for ident in idents.iter() {
            println!("  {}", ident)
        }
    }

    let question = format!("{} {} package(s) to channel '{}'. Continue?",
                           promoting_demoting,
                           idents.len(),
                           channel);

    if !ui.prompt_yes_no(&question, Some(true))? {
        ui.fatal("Aborted")?;
        return Ok(());
    }

    ui.status(changing_status,
              format!("job group {} {} channel '{}'", group_id, to_from, channel))?;

    match api_client.job_group_promote_or_demote(gid, &idents, channel, token, promote) {
        Ok(_) => {
            ui.status(changed_status,
                      format!("job group {} {} channel '{}'", group_id, to_from, channel))?;
        }
        Err(api_client::Error::APIError(StatusCode::UnprocessableEntity, _)) => {
            return Err(Error::JobGroupPromoteOrDemoteUnprocessable(promote));
        }
        Err(e) => {
            return Err(Error::JobGroupPromoteOrDemote(e, promote));
        }
    };

    Ok(())
}

#[cfg(test)]
mod test {
    use std::{io::{self,
                   Cursor,
                   Write},
              sync::{Arc,
                     RwLock}};
    use termcolor::ColorChoice;

    use super::get_ident_list;
    use crate::{api_client::{Project,
                             SchedulerResponse},
                common::ui::UI};

    fn sample_project_list() -> Vec<Project> {
        let project1 = Project { name:   "Project1".to_string(),
                                 ident:  "core/project1/1.0.0/20180101000000".to_string(),
                                 state:  "Success".to_string(),
                                 job_id: "12345678".to_string(),
                                 target: "x86_64-linux".to_string(), };
        let project2 = Project { name:   "Project2".to_string(),
                                 ident:  "core/project2/1.0.0/20180101000000".to_string(),
                                 state:  "Success".to_string(),
                                 job_id: "12345678".to_string(),
                                 target: "x86_64-linux".to_string(), };

        vec![project1, project2]
    }

    fn ui() -> (UI, OutputBuffer, OutputBuffer) {
        let stdout_buf = OutputBuffer::new();
        let stderr_buf = OutputBuffer::new();

        let ui = UI::with_streams(Box::new(io::empty()),
                                  || Box::new(stdout_buf.clone()),
                                  || Box::new(stderr_buf.clone()),
                                  ColorChoice::Never,
                                  false);

        (ui, stdout_buf, stderr_buf)
    }

    #[derive(Clone)]
    pub struct OutputBuffer {
        pub cursor: Arc<RwLock<Cursor<Vec<u8>>>>,
    }

    impl OutputBuffer {
        fn new() -> Self {
            OutputBuffer { cursor: Arc::new(RwLock::new(Cursor::new(Vec::new()))), }
        }
    }

    impl Write for OutputBuffer {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.cursor
                .write()
                .expect("Cursor lock is poisoned")
                .write(buf)
        }

        fn flush(&mut self) -> io::Result<()> {
            self.cursor
                .write()
                .expect("Cursor lock is poisoned")
                .flush()
        }
    }

    #[test]
    fn test_get_ident_list() {
        let (mut ui, _stdout, _stderr) = ui();
        let group_status = SchedulerResponse { id:           "12345678".to_string(),
                                               state:        "Finished".to_string(),
                                               projects:     sample_project_list(),
                                               created_at:
                                                   "Properly formated timestamp".to_string(),
                                               project_name: "Test Project".to_string(),
                                               target:       "x86_64-linux".to_string(), };

        let ident_list =
            get_ident_list(&mut ui, &group_status, Some("core"), false).expect("Error fetching \
                                                                                ident list");

        assert_eq!(ident_list,
                   ["core/project1/1.0.0/20180101000000",
                    "core/project2/1.0.0/20180101000000",])
    }
}
<|endoftext|>


<commit_before>extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use chrono::offset::TimeZone;
use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred,
}

impl TaskState {
    fn is_open(self) -> bool {
        match self {
            TaskState::Todo | TaskState::InProgress | TaskState::Deferred => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for TaskState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let value = match self {
            TaskState::Todo => " ",
            TaskState::InProgress => "•",
            TaskState::Complete => "✔",
            TaskState::Canceled => "-",
            TaskState::Deferred => "⟫",
        };
        write!(f, "{}", value)
    }
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum Action {
    Create {
        task: String,
        #[serde(default)]
        notes: Option<String>,
        #[serde(default)]
        tags: Vec<String>,
    },
    EditTags {
        add: Vec<String>,
        remove: Vec<String>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct TaskUpdate {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
}

impl TaskUpdate {
    fn from_str(line: &str) -> serde_json::Result<Self> {
        serde_json::from_str(line).map_err(|e| {
            println!("nuts: {:?}", e);
            e
        })
    }

    /// Create a basic TaskUpdate. The `id` is left unset and must be set before being written.
    fn new_todo(
        id: String,
        task: String,
        notes: Option<String>,
        tags: Vec<String>,
    ) -> Self {
        Self {
            id: id,
            timestamp: Utc::now(),
            action: Action::Create { task, notes, tags },
        }
    }
}

#[derive(Clone, Debug)]
struct Task {
    id: String,
    created_at: DateTime<Utc>,
    completed_at: Option<DateTime<Utc>>,
    state: TaskState,
    task: String,
    notes: Option<String>,
    tags: HashSet<String>,
}

impl Task {
    fn apply(&mut self, update: &TaskUpdate) {
        match &update.action {
            Action::Create { task, notes, tags } => {
                self.task = task.clone();
                self.notes = notes.clone();
                for tag in tags.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTags { add, remove } => {
                for tag in remove.iter() {
                    self.tags.remove(tag);
                }

                for tag in add.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTask(task) => self.task = task.clone(),
            Action::EditNotes(notes) => self.notes = notes.clone(),
            Action::EditState(state) => {
                self.state = state.clone();
                if state == &TaskState::Complete {
                    self.completed_at = Some(update.timestamp);
                }
            }
        }
    }

    fn summary(&self) -> String {
        format!("{} [{}] {}", self.id, self.state, self.task)
    }
}

impl From<&TaskUpdate> for Task {
    fn from(update: &TaskUpdate) -> Self {
        let mut task = Task {
            id: update.id.clone(),
            created_at: update.timestamp.clone(),
            completed_at: None,
            state: TaskState::Todo,
            task: "untitled".to_string(),
            notes: None,
            tags: HashSet::new(),
        };

        task.apply(update);
        return task;
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    path: String,

    timestamp: DateTime<Utc>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name: String = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let timestamp = Utc
            .datetime_from_str(&name, "%Y%m%d_%H%M%S.sup")
            .unwrap_or_else(|_| {
                println!("{:?} appears to be improperly named!", path);
                Utc.timestamp(0, 0)
            });

        // TODO: Support versioning in line
        SupdateLog {
            name,
            path: path.to_string_lossy().into(),
            timestamp,
        }
    }

    fn write_update(&self, task: &TaskUpdate) -> std::io::Result<()> {
        let mut file = self.open_file()?;
        file.seek(SeekFrom::End(0)).expect("could not seek to end");

        serde_json::ser::to_writer(&file, task)?;
        file.write_all(b"\n")?;

        Ok(())
    }

    fn iter(&self) -> SupdateIterator {
        let mut file = self.open_file().expect("could not open file");
        file.seek(SeekFrom::Start(0)).expect("could not rewind");

        SupdateIterator {
            reader: BufReader::new(file),
        }
    }

    /// Open the log file or create it if it doesn't exist.
    fn open_file(&self) -> std::io::Result<File> {
        OpenOptions::new()
            .read(true)
            .append(true)
            .create(true)
            .open(&self.path)
    }
}

struct SupdateIterator {
    reader: BufReader<File>,
}

impl Iterator for SupdateIterator {
    type Item = TaskUpdate;

    fn next(&mut self) -> Option<TaskUpdate> {
        let mut line = String::new();
        return match self.reader.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                //println!("line = {:?}", line);
                // TODO: alert when we don't succeed at parsing
                TaskUpdate::from_str(&line).ok()
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * TODO: Support concept of `rollup` files to put an upper limit on
 * how far we need to look back to fully reconstruct the state of
 * everything.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn open_journal(&self) -> Journal {
        let updates = self.list_update_logs();
        self.build_journal(updates)
    }

    fn build_journal(&self, updates: Vec<SupdateLog>) -> Journal {
        let mut journal = Journal::new();

        // Reverse ordering so that we consume oldest elements first.
        for log in updates.into_iter().rev() {
            println!("reading: {:?}", log);

            for ref update in log.iter() {
                journal.add(update);
            }
        }

        journal
    }

    fn list_update_logs(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("**/*.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }

    fn active_update_log(&self) -> SupdateLog {
        let now = Utc::now();

        let last_update = self.list_update_logs().pop();

        // Use the last update if it's still the same day, otherwise create a new one.
        last_update
            .filter(|log| log.timestamp.date() == now.date())
            .unwrap_or_else(|| {
                let log_path = now.format("%Y/%m/%Y%m%d_%H%M%S.sup").to_string();
                let abs_path = Path::new(&self.config.path).join(log_path);

                let dir = abs_path.parent().unwrap();

                std::fs::create_dir_all(dir).expect("create directory failed");

                SupdateLog::new(&abs_path)
            })
    }
}

/**
 * A collection of Updates.
 *
 * Represents the cumulative, "rolled up" set of task updates.
 */
#[derive(Debug)]
struct Journal {
    /// id -> task
    entries: BTreeMap<String, Task>,
    last_id: u64,
}

impl Journal {
    fn new() -> Self {
        return Self {
            entries: BTreeMap::new(),
            last_id: 0,
        };
    }

    fn add(&mut self, update: &TaskUpdate) {
        let id = &update.id;

        // Keep track of higest id value we've seen.
        let id_int = id.parse::<u64>().unwrap();
        self.last_id = std::cmp::max(self.last_id, id_int);

        let task = self.entries.get_mut(id);

        match task {
            Some(t) => t.apply(update),
            None => {
                self.entries.insert(id.clone(), Task::from(update));
            }
        }
    }

    fn next_id(&mut self) -> String {
        self.last_id += 1;

        format!("{:04}", self.last_id)
    }

    // TODO: Hack until a custom iterator is implemented.
    fn for_each(&self, f: &Fn(&Task) -> ()) {
        self.entries.values().for_each(f)
    }
}

struct SupCli {
    matches: ArgMatches<'static>,
}

impl SupCli {
    fn new() -> Self {
        Self {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("todo")
                    .about("create a new todo item")
                    .arg(Arg::with_name("TASK").takes_value(true).multiple(true)),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("asdf")
                    .arg(Arg::with_name("DATE").takes_value(true).help("asdf")),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config().expect("error in config file");
        let mut app = SupApp::new(&cfg);

        match self.matches.subcommand() {
            ("show", Some(m)) => println!("TODO: show {:?}", m),
            ("serve", Some(m)) => println!("TODO: serve {:?}", m),
            ("todo", Some(m)) => {
                let task = m.values_of("TASK").map(|v| {
                    let vec = v.collect::<Vec<_>>();
                    vec.join(" ")
                });

                app.run_add_task(task)?;
            }

            _ => app.run_show_updates()?,
        };

        Ok(())
    }
}

struct SupApp {
    repo: Repository,
}

impl SupApp {
    fn new(cfg: &Supfile) -> Self {
        let repo = Repository::new(cfg.repo.clone());

        Self { repo }
    }

    fn run_add_task(&mut self, task: Option<String>) -> Result<(), Box<dyn Error>> {
        let mut journal = self.repo.open_journal();

        let task = task.expect("TODO: ui task creation");
        let id = journal.next_id();

        let update = TaskUpdate::new_todo(id, task, None, vec![]);
        self.repo.active_update_log().write_update(&update)?;

        self.run_show_updates()
    }

    fn run_show_updates(&self) -> Result<(), Box<dyn Error>> {
        let journal = self.repo.open_journal();
        journal.for_each(&|task| {
            println!("{}", task.summary());
        });

        Ok(())
    }
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupCli::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<commit_msg>Add an overly-simplified show task<commit_after>extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use chrono::offset::TimeZone;
use chrono::{DateTime, Utc};
use clap::{App, Arg, ArgMatches, SubCommand};
use glob::glob;
use serde_derive::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Clone)]
struct RepoConfig {
    path: String,
    #[serde(default = "default_false")]
    commit: bool,
    #[serde(default = "default_false")]
    sync_updates: bool,
}

fn default_false() -> bool {
    false
}

#[derive(Deserialize, Debug)]
struct Supfile {
    repo: RepoConfig,
    editor: Option<String>,
}

impl Supfile {
    pub fn from(str: &str) -> Result<Self, toml::de::Error> {
        toml::from_str(&str)
    }

    pub fn find() -> PathBuf {
        // TODO: pull in XDG crate for paths
        let default_dir = "~/.config/sup/";

        ["./", default_dir]
            .iter()
            .map(|dir| Path::new(dir).join("Supfile"))
            .find(|p| p.exists())
            .unwrap_or_else(|| {
                // TODO: offer to create path somehow
                panic!("can't find supfile")
            })
    }
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum TaskState {
    Todo,
    InProgress,
    Complete,
    Canceled,
    Deferred,
}

impl TaskState {
    fn is_open(self) -> bool {
        match self {
            TaskState::Todo | TaskState::InProgress | TaskState::Deferred => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for TaskState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let value = match self {
            TaskState::Todo => " ",
            TaskState::InProgress => "•",
            TaskState::Complete => "✔",
            TaskState::Canceled => "-",
            TaskState::Deferred => "⟫",
        };
        write!(f, "{}", value)
    }
}

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum Action {
    Create {
        task: String,
        #[serde(default)]
        notes: Option<String>,
        #[serde(default)]
        tags: Vec<String>,
    },
    EditTags {
        add: Vec<String>,
        remove: Vec<String>,
    },
    EditTask(String),
    EditNotes(Option<String>),
    EditState(TaskState),
}

#[derive(Deserialize, Serialize)]
struct TaskUpdate {
    id: String,
    timestamp: DateTime<Utc>,
    action: Action,
}

impl TaskUpdate {
    fn from_str(line: &str) -> serde_json::Result<Self> {
        serde_json::from_str(line).map_err(|e| {
            println!("nuts: {:?}", e);
            e
        })
    }

    /// Create a basic TaskUpdate. The `id` is left unset and must be set before being written.
    fn new_todo(
        id: String,
        task: String,
        notes: Option<String>,
        tags: Vec<String>,
    ) -> Self {
        Self {
            id: id,
            timestamp: Utc::now(),
            action: Action::Create { task, notes, tags },
        }
    }
}

#[derive(Clone, Debug)]
struct Task {
    id: String,
    created_at: DateTime<Utc>,
    completed_at: Option<DateTime<Utc>>,
    state: TaskState,
    task: String,
    notes: Option<String>,
    tags: HashSet<String>,
}

impl Task {
    fn apply(&mut self, update: &TaskUpdate) {
        match &update.action {
            Action::Create { task, notes, tags } => {
                self.task = task.clone();
                self.notes = notes.clone();
                for tag in tags.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTags { add, remove } => {
                for tag in remove.iter() {
                    self.tags.remove(tag);
                }

                for tag in add.iter() {
                    self.tags.insert(tag.clone());
                }
            }
            Action::EditTask(task) => self.task = task.clone(),
            Action::EditNotes(notes) => self.notes = notes.clone(),
            Action::EditState(state) => {
                self.state = state.clone();
                if state == &TaskState::Complete {
                    self.completed_at = Some(update.timestamp);
                }
            }
        }
    }

    fn summary(&self) -> String {
        format!("{} [{}] {}", self.id, self.state, self.task)
    }
}

impl From<&TaskUpdate> for Task {
    fn from(update: &TaskUpdate) -> Self {
        let mut task = Task {
            id: update.id.clone(),
            created_at: update.timestamp.clone(),
            completed_at: None,
            state: TaskState::Todo,
            task: "untitled".to_string(),
            notes: None,
            tags: HashSet::new(),
        };

        task.apply(update);
        return task;
    }
}

#[derive(Debug)]
struct SupdateLog {
    name: String,
    path: String,

    timestamp: DateTime<Utc>,
}

impl SupdateLog {
    fn new(path: &Path) -> Self {
        let name: String = path
            .file_name()
            .expect("need file, not directory")
            .to_string_lossy()
            .into();

        let timestamp = Utc
            .datetime_from_str(&name, "%Y%m%d_%H%M%S.sup")
            .unwrap_or_else(|_| {
                println!("{:?} appears to be improperly named!", path);
                Utc.timestamp(0, 0)
            });

        // TODO: Support versioning in line
        SupdateLog {
            name,
            path: path.to_string_lossy().into(),
            timestamp,
        }
    }

    fn write_update(&self, task: &TaskUpdate) -> std::io::Result<()> {
        let mut file = self.open_file()?;
        file.seek(SeekFrom::End(0)).expect("could not seek to end");

        serde_json::ser::to_writer(&file, task)?;
        file.write_all(b"\n")?;

        Ok(())
    }

    fn iter(&self) -> SupdateIterator {
        let mut file = self.open_file().expect("could not open file");
        file.seek(SeekFrom::Start(0)).expect("could not rewind");

        SupdateIterator {
            reader: BufReader::new(file),
        }
    }

    /// Open the log file or create it if it doesn't exist.
    fn open_file(&self) -> std::io::Result<File> {
        OpenOptions::new()
            .read(true)
            .append(true)
            .create(true)
            .open(&self.path)
    }
}

struct SupdateIterator {
    reader: BufReader<File>,
}

impl Iterator for SupdateIterator {
    type Item = TaskUpdate;

    fn next(&mut self) -> Option<TaskUpdate> {
        let mut line = String::new();
        return match self.reader.read_line(&mut line) {
            // End of file
            Ok(0) => None,

            Ok(_) => {
                //println!("line = {:?}", line);
                // TODO: alert when we don't succeed at parsing
                TaskUpdate::from_str(&line).ok()
            }

            Err(e) => {
                println!("Failed to read line: {:?}", e);
                None
            }
        };
    }
}

/**
 * A Repository represents a directory containing SupdateLogs.
 *
 * The directory structure is something like this:
 *
 * repo/
 *   2019/
 *     08/
 *       20190801_235959.sup
 *       20190802_003030.sup
 *       20190802_123030.sup
 *
 * TODO: Support concept of `rollup` files to put an upper limit on
 * how far we need to look back to fully reconstruct the state of
 * everything.
 */

struct Repository {
    config: RepoConfig,
}

impl Repository {
    fn new(cfg: RepoConfig) -> Self {
        // TODO: Handle error, return Result
        if !Path::new(&cfg.path).exists() {
            panic!("given repository does not exist")
        }

        Repository { config: cfg }
    }

    fn open_journal(&self) -> Journal {
        let updates = self.list_update_logs();
        self.build_journal(updates)
    }

    fn build_journal(&self, updates: Vec<SupdateLog>) -> Journal {
        let mut journal = Journal::new();

        // Reverse ordering so that we consume oldest elements first.
        for log in updates.into_iter().rev() {
            for ref update in log.iter() {
                journal.add(update);
            }
        }

        journal
    }

    fn list_update_logs(&self) -> Vec<SupdateLog> {
        let path = Path::new(&self.config.path).join("**/*.sup");

        // TODO: This is really wasteful. Make this an iterator
        // Get list of all files in the repo
        let mut updates = glob(path.to_str().unwrap())
            .expect("glob failed")
            .filter_map(|f| f.map(|p| SupdateLog::new(&p)).ok())
            .collect::<Vec<SupdateLog>>();

        // We want sorted (chronological, desc) order, based on file name.
        updates.sort_by(|a, b| b.name.cmp(&a.name));
        return updates;
    }

    fn active_update_log(&self) -> SupdateLog {
        let now = Utc::now();

        let last_update = self.list_update_logs().pop();

        // Use the last update if it's still the same day, otherwise create a new one.
        last_update
            .filter(|log| log.timestamp.date() == now.date())
            .unwrap_or_else(|| {
                let log_path = now.format("%Y/%m/%Y%m%d_%H%M%S.sup").to_string();
                let abs_path = Path::new(&self.config.path).join(log_path);

                let dir = abs_path.parent().unwrap();

                std::fs::create_dir_all(dir).expect("create directory failed");

                SupdateLog::new(&abs_path)
            })
    }
}

/**
 * A collection of Updates.
 *
 * Represents the cumulative, "rolled up" set of task updates.
 */
#[derive(Debug)]
struct Journal {
    /// id -> task
    tasks: BTreeMap<String, Task>,
    last_id: u64,
}

impl Journal {
    fn new() -> Self {
        return Self {
            tasks: BTreeMap::new(),
            last_id: 0,
        };
    }

    fn add(&mut self, update: &TaskUpdate) {
        let id = &update.id;

        // Keep track of higest id value we've seen.
        let id_int = id.parse::<u64>().unwrap();
        self.last_id = std::cmp::max(self.last_id, id_int);

        let task = self.tasks.get_mut(id);

        match task {
            Some(t) => t.apply(update),
            None => {
                self.tasks.insert(id.clone(), Task::from(update));
            }
        }
    }

    fn get_task(&self, id: &String) -> Option<&Task> {
        self.tasks.get(id)
    }

    // TODO: Hack until a custom iterator is implemented.
    fn for_each(&self, f: &Fn(&Task) -> ()) {
        self.tasks.values().for_each(f)
    }

    fn next_id(&mut self) -> String {
        self.last_id += 1;

        format!("{:04}", self.last_id)
    }
}

struct SupCli {
    matches: ArgMatches<'static>,
}

impl SupCli {
    fn new() -> Self {
        Self {
            matches: Self::parse_args(),
        }
    }

    fn parse_args<'a>() -> ArgMatches<'a> {
        App::new("sup")
            .version("0.0.0")
            .about("Keep track of what's up.")
            .arg(
                Arg::with_name("config")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .takes_value(true)
                    .help("Set custom location for Supfile"),
            )
            .subcommand(
                SubCommand::with_name("todo")
                    .about("create a new todo item")
                    .arg(Arg::with_name("TASK").takes_value(true).multiple(true)),
            )
            .subcommand(
                SubCommand::with_name("show")
                    .about("show full details of task")
                    .arg(
                        Arg::with_name("TASK_ID")
                            .required(true)
                            .takes_value(true)
                            .help("asdf"),
                    ),
            )
            .subcommand(
                SubCommand::with_name("serve")
                    .about("launch web interface")
                    .arg(
                        Arg::with_name("port")
                            .short("p")
                            .long("port")
                            .takes_value(true),
                    ),
            )
            .get_matches()
    }

    fn load_config(&self) -> Result<Supfile, Box<dyn Error>> {
        let path = self
            .matches
            .value_of("config")
            .map(|p| Path::new(p).to_path_buf())
            .unwrap_or_else(Supfile::find);

        let bytes = std::fs::read_to_string(path.as_os_str())?;
        Ok(Supfile::from(&bytes)?)
    }

    fn run(&self) -> Result<(), Box<dyn Error>> {
        let cfg = self.load_config().expect("error in config file");
        let mut app = SupApp::new(&cfg);

        match self.matches.subcommand() {
            ("show", Some(m)) => {
                let id = m.value_of("TASK_ID").unwrap();

                app.run_show_task(id.to_string())
            }
            ("todo", Some(m)) => {
                let task = m.values_of("TASK").map(|v| {
                    let vec = v.collect::<Vec<_>>();
                    vec.join(" ")
                });

                app.run_add_task(task)
            }

            _ => app.run_show_updates(),
        }
    }
}

struct SupApp {
    repo: Repository,
}

impl SupApp {
    fn new(cfg: &Supfile) -> Self {
        let repo = Repository::new(cfg.repo.clone());

        Self { repo }
    }

    fn run_add_task(&mut self, task: Option<String>) -> Result<(), Box<dyn Error>> {
        let mut journal = self.repo.open_journal();

        let task = task.expect("TODO: ui task creation");
        let id = journal.next_id();

        let update = TaskUpdate::new_todo(id, task, None, vec![]);
        self.repo.active_update_log().write_update(&update)?;

        self.run_show_updates()
    }

    fn run_show_task(&self, id: String) -> Result<(), Box<dyn Error>> {
        let mut journal = self.repo.open_journal();

        let task = journal.get_task(&id).expect("unknown task id");

        println!(
            "{} {}

{}

tags: {}
created: {}
completed: {:?}",
            task.state,
            task.task,
            &task.notes.clone().unwrap_or("[no details]".to_string()), // TODO: try
            "".to_string(), // task.tags.iter().collect::<Vec<_>>().join(", "),
            task.created_at,
            task.completed_at
        );

        Ok(())
    }

    fn run_show_updates(&self) -> Result<(), Box<dyn Error>> {
        let journal = self.repo.open_journal();
        journal.for_each(&|task| {
            println!("{}", task.summary());
        });

        Ok(())
    }
}

// TODO: Investiage this instead of Box<Error>
// https://github.com/rust-lang-nursery/failure
fn main() {
    let app = SupCli::new();
    match app.run() {
        Err(e) => println!("oh no! {:?}", e),
        Ok(_) => (),
    };
}
<|endoftext|>


<commit_before>use std::thread;
use std::time::Duration;

fn main() {
    let simulated_user_specified_value = 3;
    let simulated_random_number = 4;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn simulated_expensive_calculation(intensity: i32) -> i32 {
    println!("Very expensive calculation...");
    thread::sleep(Duration::from_secs(2));
    intensity
}

fn generate_workout(intensity: i32, random_number: i32) {
    let expensive_closure = |num| {
        println!("Very expensive calculation...");
        thread::sleep(Duration::from_secs(2));
        num
    };

    if intensity < 25 {
        println!("Do {} pushups!", expensive_closure(intensity));
        println!("Next, do {} situps!", expensive_closure(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break!");
        } else {
            println!("Run for {} minutes!", expensive_closure(intensity));
        }
    }
}
<commit_msg>Implement Cacher struct<commit_after>use std::thread;
use std::time::Duration;

fn main() {
    let simulated_user_specified_value = 3;
    let simulated_random_number = 4;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn simulated_expensive_calculation(intensity: i32) -> i32 {
    println!("Very expensive calculation...");
    thread::sleep(Duration::from_secs(2));
    intensity
}

struct Cacher<T>
    where T: Fn(i32) -> i32
{
    calculation: T,
    value: Option<i32>
}

fn generate_workout(intensity: i32, random_number: i32) {
    let expensive_closure = |num| {
        println!("Very expensive calculation...");
        thread::sleep(Duration::from_secs(2));
        num
    };

    if intensity < 25 {
        println!("Do {} pushups!", expensive_closure(intensity));
        println!("Next, do {} situps!", expensive_closure(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break!");
        } else {
            println!("Run for {} minutes!", expensive_closure(intensity));
        }
    }
}
<|endoftext|>


<commit_before>use std::rc::{Rc};
use std::cell::{RefCell};

pub struct Broker<T: Subscriber> {
    subscriptions: Subscriptions<T>,
}

impl<T: Subscriber> Broker<T> {
    pub fn new() -> Self {
        Broker{ subscriptions: Subscriptions::<T>::new() }
    }

    pub fn publish(&mut self, topic: &str, payload: &[u8]) {
        self.subscriptions.publish(topic, payload);
    }

    pub fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        self.subscriptions.subscribe(subscriber, &topics)
    }
}

//is same identity
fn is_same<T>(lhs: &T, rhs: &T) -> bool {
    return lhs as *const T as i64 == rhs as *const T as i64
}

pub struct Subscriptions<T: Subscriber> {
    subscribers: Vec<Rc<RefCell<T>>>,
}

impl<T: Subscriber> Subscriptions<T> {
    fn new() -> Self {
        return Subscriptions { subscribers: vec![], }
    }

    fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        let subscriber = self.add_or_find(subscriber);
        subscriber.borrow_mut().append_topics(topics);
    }

    fn add_or_find(&mut self, subscriber: Rc<RefCell<T>>) -> Rc<RefCell<T>> {
        for sub in &self.subscribers {
            let sub1 = sub.clone();
            let sub2 = subscriber.clone();
            //this is horrible
            //borrow doesn't return T, it returns Ref<T>
            //since Ref<T> itself doesn't satisfy the template contraint, we need to deref
            //it to get the to the T inside. And since is_same takes borrows, we reapply
            //the ampersand
            if is_same(&*sub1.borrow(), &*sub2.borrow()) {
                return sub2;
            }
        }

        self.subscribers.push(subscriber);
        self.subscribers[self.subscribers.len() - 1].clone()
    }

    fn publish(&mut self, topic: &str, payload: &[u8]) {
        for subscriber in &mut self.subscribers {
            let subscriber = subscriber.clone();
            let mut subscriber = subscriber.borrow_mut();

            for _ in subscriber.topics().iter().filter(|&t| topic_matches(&topic, &t)) {
                subscriber.new_message(payload);
            }
        }
    }
}

fn topic_matches(pub_topic: &str, sub_topic: &str) -> bool {
    pub_topic == sub_topic
}

pub trait Subscriber {
    fn new_message(&mut self, bytes: &[u8]);
    fn append_topics(&mut self, topics: &[&str]);
    fn topics(&self) -> Vec<String>;
}

pub trait SubscriberT {
    fn new_message(&mut self, bytes: &[u8]);
}

struct Subscription<T: SubscriberT> {
    subscriber: Rc<RefCell<T>>,
    topics: Vec<String>,
}

impl<T: SubscriberT> Subscription<T> {
    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
    }

    fn topics(&self) -> Vec<String> {
        self.topics.clone()
    }
}

#[cfg(test)]
struct TestSubscriber {
    msgs: Vec<Vec<u8>>,
    topics: Vec<String>,
}
#[cfg(test)]
impl TestSubscriber {
    fn new() -> Self {
        return TestSubscriber{msgs: vec![], topics: vec![]}
    }
}

#[cfg(test)]
impl Subscriber for TestSubscriber {

    fn new_message(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }

    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
    }

    fn topics(&self) -> Vec<String> {
        self.topics.clone()
    }
}

#[test]
fn test_subscribe() {
    let mut broker = Broker::<TestSubscriber>::new();
    let sub_ref = Rc::new(RefCell::new(TestSubscriber::new()));
    let subscriber = sub_ref.clone();
    broker.publish("topics/foo", &[0, 1, 2]);
    assert_eq!(subscriber.borrow().msgs.len(), 0);

    broker.subscribe(subscriber.clone(), &["topics/foo"]);
    broker.publish("topics/foo", &[0, 1, 9]);
    broker.publish("topics/bar", &[2, 4, 6]);
    assert_eq!(subscriber.borrow().msgs.len(), 1);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);

    broker.subscribe(subscriber.clone(), &["topics/bar"]);
    broker.publish("topics/foo", &[1, 3, 5, 7]);
    broker.publish("topics/bar", &[2, 4]);
    assert_eq!(subscriber.borrow().msgs.len(), 3);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);
    assert_eq!(subscriber.borrow().msgs[1], &[1, 3, 5, 7]);
    assert_eq!(subscriber.borrow().msgs[2], &[2, 4]);
}
<commit_msg>At least it compiles<commit_after>use std::rc::{Rc};
use std::cell::{RefCell};
use std::collections::HashMap;

pub struct Broker<T: Subscriber> {
    subscriptions: Subscriptions<T>,
}

impl<T: Subscriber> Broker<T> {
    pub fn new() -> Self {
        Broker{ subscriptions: Subscriptions::<T>::new() }
    }

    pub fn publish(&mut self, topic: &str, payload: &[u8]) {
        self.subscriptions.publish(topic, payload);
    }

    pub fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        self.subscriptions.subscribe(subscriber, &topics)
    }
}

pub struct Subscriptions<T: Subscriber> {
    subscribers: Vec<Rc<RefCell<T>>>,
}

impl<T: Subscriber> Subscriptions<T> {
    fn new() -> Self {
        return Subscriptions { subscribers: vec![], }
    }

    fn subscribe(&mut self, subscriber: Rc<RefCell<T>>, topics: &[&str]) {
        let subscriber = self.add_or_find(subscriber);
        subscriber.borrow_mut().append_topics(topics);
    }

    fn add_or_find(&mut self, subscriber: Rc<RefCell<T>>) -> Rc<RefCell<T>> {
        for sub in &self.subscribers {
            let sub1 = sub.clone();
            let sub2 = subscriber.clone();
            //this is horrible
            //borrow doesn't return T, it returns Ref<T>
            //since Ref<T> itself doesn't satisfy the template contraint, we need to deref
            //it to get the to the T inside. And since is_same takes borrows, we reapply
            //the ampersand
            if is_same(&*sub1.borrow(), &*sub2.borrow()) {
                return sub2;
            }
        }

        self.subscribers.push(subscriber);
        self.subscribers[self.subscribers.len() - 1].clone()
    }

    fn publish(&mut self, topic: &str, payload: &[u8]) {
        for subscriber in &mut self.subscribers {
            let subscriber = subscriber.clone();
            let mut subscriber = subscriber.borrow_mut();

            for _ in subscriber.topics().iter().filter(|&t| topic_matches(&topic, &t)) {
                subscriber.new_message(payload);
            }
        }
    }
}

//is same identity
fn is_same<T>(lhs: &T, rhs: &T) -> bool {
    return lhs as *const T as i64 == rhs as *const T as i64
}

fn topic_matches(pub_topic: &str, sub_topic: &str) -> bool {
    pub_topic == sub_topic
}

pub trait Subscriber {
    fn new_message(&mut self, bytes: &[u8]);
    fn append_topics(&mut self, topics: &[&str]);
    fn topics(&self) -> Vec<String>;
}

pub trait SubscriberT {
    fn new_message(&mut self, bytes: &[u8]);
}

struct Subscription<T: SubscriberT> {
    subscriber: Rc<RefCell<T>>,
    topics: Vec<String>,
}

impl<T: SubscriberT> Subscription<T> {
    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
    }

    fn topics(&self) -> Vec<String> {
        self.topics.clone()
    }
}

struct Node<T: SubscriberT> {
    children: HashMap<String, Box<Node<T>>>,
    leaves: Vec<Subscription<T>>,
}

pub struct SubscriptionsT<T: SubscriberT> {
    tree: Node<T>,
}

impl<T: SubscriberT> SubscriptionsT<T> {
    fn new() -> Self {
        SubscriptionsT { tree: Node { children: HashMap::new(), leaves: vec![] }}
    }
}

#[cfg(test)]
struct TestSubscriberT {
    msgs: Vec<Vec<u8>>,
}

#[cfg(test)]
impl TestSubscriberT {
    fn new() -> Self {
        return TestSubscriberT{msgs: vec![]}
    }
}

#[cfg(test)]
impl SubscriberT for TestSubscriberT {
    fn new_message(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }
}

#[test]
fn test_subt() {
    let mut subs = SubscriptionsT::<TestSubscriberT>::new();
}

#[cfg(test)]
struct TestSubscriber {
    msgs: Vec<Vec<u8>>,
    topics: Vec<String>,
}
#[cfg(test)]
impl TestSubscriber {
    fn new() -> Self {
        return TestSubscriber{msgs: vec![], topics: vec![]}
    }
}

#[cfg(test)]
impl Subscriber for TestSubscriber {

    fn new_message(&mut self, bytes: &[u8]) {
        self.msgs.push(bytes.to_vec());
    }

    fn append_topics(&mut self, topics: &[&str]) {
        let mut new_topics = topics.to_vec().into_iter().map(|t| t.to_string()).collect();
        self.topics.append(&mut new_topics);
    }

    fn topics(&self) -> Vec<String> {
        self.topics.clone()
    }
}

#[test]
fn test_subscribe() {
    let mut broker = Broker::<TestSubscriber>::new();
    let sub_ref = Rc::new(RefCell::new(TestSubscriber::new()));
    let subscriber = sub_ref.clone();
    broker.publish("topics/foo", &[0, 1, 2]);
    assert_eq!(subscriber.borrow().msgs.len(), 0);

    broker.subscribe(subscriber.clone(), &["topics/foo"]);
    broker.publish("topics/foo", &[0, 1, 9]);
    broker.publish("topics/bar", &[2, 4, 6]);
    assert_eq!(subscriber.borrow().msgs.len(), 1);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);

    broker.subscribe(subscriber.clone(), &["topics/bar"]);
    broker.publish("topics/foo", &[1, 3, 5, 7]);
    broker.publish("topics/bar", &[2, 4]);
    assert_eq!(subscriber.borrow().msgs.len(), 3);
    assert_eq!(subscriber.borrow().msgs[0], &[0, 1, 9]);
    assert_eq!(subscriber.borrow().msgs[1], &[1, 3, 5, 7]);
    assert_eq!(subscriber.borrow().msgs[2], &[2, 4]);
}

#[test]
fn test_tree() {

}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::io::Read;
use std::collections::HashMap;
use logging;
use mode::Command;
use std::cmp;
use std::iter::FromIterator;

use errors::CrbError;

// A reference to a position.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Anchor {
    id: i64,
}

#[derive(PartialEq, Eq)]
pub struct Wrap {
    style: WrapStyle,
    width: i32,
    // Whether the cursor moves between visual lines or buffer lines.
    vismove: bool,
}

#[derive(PartialEq, Eq)]
pub enum WrapStyle {
    Truncate,
    Hard,
    Word,
}

impl Wrap {
    pub fn default(width: i32) -> Wrap {
        Wrap {
            style: WrapStyle::Truncate,
            width: width,
            vismove: false,
        }
    }
}

// Private structure containing position data.
#[derive(Clone)]
struct Position {
    line: i32,
    // Offset from the beginning of the line.
    // 0 is before the first character.
    // len(line) is after the last character.
    offset: i32,
}

struct Line {
    text: String,
}

#[derive(Debug)]
pub struct Display {
    pub x: i32,
    pub y: i32,
    pub symbol: Symbol,
}

#[derive(Debug)]
pub enum Symbol {
    Char(char),
    Anchor(Anchor),
    Void,
}

pub struct Buffer {
    contents: Vec<Line>,
    pub file_path: Option<String>,
    pub unsaved: bool,
    pub newfile: bool,

    // Map from anchor id to position.
    anchors: HashMap<i64, Position>,
    next_anchor_id: i64,
}

impl Buffer {
    /** Creators **/

    pub fn load_from_file(path: &str) -> Result<Buffer, io::Error> {
        let s = try!(read_file(path));
        let contents: Vec<Line> = s.split("\n")
            .map(|x| Line { text: x.to_string() })
            .collect();
        let mut buf = Buffer::empty();
        buf.contents = contents;
        buf.file_path = Some(path.to_string());
        buf.newfile;
        Ok(buf)
    }

    pub fn new_file(path: &str) -> Buffer {
        let mut buf = Buffer::empty();
        buf.file_path = Some(path.to_string());
        buf
    }

    pub fn empty() -> Buffer {
        Buffer {
            contents: Vec::new(),
            file_path: None,
            unsaved: false,
            newfile: true,
            anchors: HashMap::new(),
            next_anchor_id: 0,
        }
    }

    /** Mutators **/

    pub fn new_anchor(&mut self) -> Anchor {
        let a = Anchor { id: self.new_anchor_id() };
        let p = Position {
            line: 0,
            offset: 0,
        };
        self.anchors.insert(a.id, p);
        a
    }

    pub fn move_anchor(&mut self, anchor: Anchor, m: &Command) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut p2: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let m = canonicalize_move(m);
        let p3: Position = match m {
            Command::MoveRight(n) => {
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                p2.offset = cmp::min(cmp::max(0, p2.offset + n), len);
                p2
            }
            Command::MoveDown(n) => {
                p2.line = cmp::min(cmp::max(0, p2.line + n), self.count_lines());
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                p2.offset = cmp::min(p2.offset, len);
                p2
            }
            _ => return Err(CrbError::new("unsupported move command")),
        };
        self.anchors.insert(anchor.id, p3);
        Ok(())
    }

    fn new_anchor_id(&mut self) -> i64 {
        self.next_anchor_id += 1;
        self.next_anchor_id - 1
    }

    /** Observers **/

    pub fn line(&self, i: i32) -> Option<&str> {
        self.contents.get(i as usize).map(|l| l.text.as_str())
    }

    pub fn count_lines(&self) -> i32 {
        self.contents.len() as i32
    }

    pub fn display(&self, start_line: i32, height: i32, wrap: Wrap) -> Vec<Display> {
        // TODO better overallocation number.
        let mut v = Vec::with_capacity((height * wrap.width + 20) as usize);
        if wrap != Wrap::default(wrap.width) {
            panic!("TODO unsupported wrap");
        }
        // For each line.
        let it = self.contents
            .iter()
            .skip(start_line as usize)
            .take(height as usize)
            .enumerate();
        for (i, line) in it {
            let i = i as i32;
            let mut linetext = line.text.chars();
            let mut charsleft = true;
            let la = self.line_anchors(i);
            let mut ancposes = la.iter();
            let mut next_ancpos = ancposes.next();
            // For each character.
            for j in 0..wrap.width {
                // Send anchor.
                loop {
                    if let Some(&(a_id, p)) = next_ancpos {
                        if p.offset == j {
                            v.push(Display {
                                x: j,
                                y: i,
                                symbol: Symbol::Anchor(Anchor { id: *a_id }),
                                // symbol: Symbol::Void,
                            });
                            next_ancpos = ancposes.next();
                        } else {
                            break;
                        }
                    } else {
                        break;
                    }
                }
                // TODO anchor off the end.

                // Send character.
                let mut s = Symbol::Void;
                // TODO anchors
                if charsleft {
                    if let Some(c) = linetext.next() {
                        s = Symbol::Char(c);
                    } else {
                        charsleft = false;
                    }
                }
                v.push(Display {
                    x: j,
                    y: i,
                    symbol: s,
                });
            }
        }
        v
    }

    fn line_anchors(&self, i: i32) -> Vec<(&i64, &Position)> {
        self.anchors
            .iter()
            .filter_map(|id_p| match id_p.1.line == i {
                true => Some(id_p),
                false => None,
            })
            .collect()
    }

    pub fn anchor_at(&self, anchor: Anchor, x: i32, y: i32) -> bool {
        self.anchors.get(&anchor.id).map_or(false, |p| {
            let mx = (p.line as i32) == y;
            let my = (p.offset as i32) == x;
            mx && my
        })
    }
}

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut f = try!(fs::File::open(path));
    let mut contents = String::new();
    try!(f.read_to_string(&mut contents));
    Ok(contents)
}

fn canonicalize_move(mov: &Command) -> Command {
    match mov.clone() {
        Command::MoveLeft(n) => Command::MoveRight(-n),
        Command::MoveUp(n) => Command::MoveDown(-n),
        m @ _ => m,
    }
}
<commit_msg>add wishful offset<commit_after>use std::fs;
use std::io;
use std::io::Read;
use std::collections::HashMap;
use logging;
use mode::Command;
use std::cmp;
use std::iter::FromIterator;

use errors::CrbError;

// A reference to a position.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Anchor {
    id: i64,
}

#[derive(PartialEq, Eq)]
pub struct Wrap {
    style: WrapStyle,
    width: i32,
    // Whether the cursor moves between visual lines or buffer lines.
    vismove: bool,
}

#[derive(PartialEq, Eq)]
pub enum WrapStyle {
    Truncate,
    Hard,
    Word,
}

impl Wrap {
    pub fn default(width: i32) -> Wrap {
        Wrap {
            style: WrapStyle::Truncate,
            width: width,
            vismove: false,
        }
    }
}

// Private structure containing position data.
#[derive(Clone)]
struct Position {
    line: i32,
    // Offset from the beginning of the line.
    // 0 is before the first character.
    // len(line) is after the last character.
    offset: i32,
    // The offset that this will snap back to when moving to a longer line.
    wishful_offset: Option<i32>,
}

struct Line {
    text: String,
}

#[derive(Debug)]
pub struct Display {
    pub x: i32,
    pub y: i32,
    pub symbol: Symbol,
}

#[derive(Debug)]
pub enum Symbol {
    Char(char),
    Anchor(Anchor),
    Void,
}

pub struct Buffer {
    contents: Vec<Line>,
    pub file_path: Option<String>,
    pub unsaved: bool,
    pub newfile: bool,

    // Map from anchor id to position.
    anchors: HashMap<i64, Position>,
    next_anchor_id: i64,
}

impl Buffer {
    /** Creators **/

    pub fn load_from_file(path: &str) -> Result<Buffer, io::Error> {
        let s = try!(read_file(path));
        let contents: Vec<Line> = s.split("\n")
            .map(|x| Line { text: x.to_string() })
            .collect();
        let mut buf = Buffer::empty();
        buf.contents = contents;
        buf.file_path = Some(path.to_string());
        buf.newfile;
        Ok(buf)
    }

    pub fn new_file(path: &str) -> Buffer {
        let mut buf = Buffer::empty();
        buf.file_path = Some(path.to_string());
        buf
    }

    pub fn empty() -> Buffer {
        Buffer {
            contents: Vec::new(),
            file_path: None,
            unsaved: false,
            newfile: true,
            anchors: HashMap::new(),
            next_anchor_id: 0,
        }
    }

    /** Mutators **/

    pub fn new_anchor(&mut self) -> Anchor {
        let a = Anchor { id: self.new_anchor_id() };
        let p = Position {
            line: 0,
            offset: 0,
            wishful_offset: None,
        };
        self.anchors.insert(a.id, p);
        a
    }

    pub fn move_anchor(&mut self, anchor: Anchor, m: &Command) -> Result<(), CrbError> {
        let err = CrbError::new("no such anchor");
        let mut p2: Position = try!(self.anchors.get(&anchor.id).ok_or(err)).clone();
        let m = canonicalize_move(m);
        let p3: Position = match m {
            Command::MoveRight(n) => {
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                p2.offset = cmp::min(cmp::max(0, p2.offset + n), len);
                p2
            }
            Command::MoveDown(n) => {
                p2.line = cmp::min(cmp::max(0, p2.line + n), self.count_lines());
                let len = self.line(p2.line).unwrap_or("").chars().count() as i32;
                let wish = match p2.wishful_offset {
                    None => p2.offset,
                    Some(x) => x,
                };
                p2.offset = cmp::min(wish, len);
                p2.wishful_offset = match p2.offset < wish {
                    true => Some(wish),
                    false => None,
                };
                p2
            }
            _ => return Err(CrbError::new("unsupported move command")),
        };
        self.anchors.insert(anchor.id, p3);
        Ok(())
    }

    fn new_anchor_id(&mut self) -> i64 {
        self.next_anchor_id += 1;
        self.next_anchor_id - 1
    }

    /** Observers **/

    pub fn line(&self, i: i32) -> Option<&str> {
        self.contents.get(i as usize).map(|l| l.text.as_str())
    }

    pub fn count_lines(&self) -> i32 {
        self.contents.len() as i32
    }

    pub fn display(&self, start_line: i32, height: i32, wrap: Wrap) -> Vec<Display> {
        // TODO better overallocation number.
        let mut v = Vec::with_capacity((height * wrap.width + 20) as usize);
        if wrap != Wrap::default(wrap.width) {
            panic!("TODO unsupported wrap");
        }
        // For each line.
        let it = self.contents
            .iter()
            .skip(start_line as usize)
            .take(height as usize)
            .enumerate();
        for (i, line) in it {
            let i = i as i32;
            let mut linetext = line.text.chars();
            let mut charsleft = true;
            let la = self.line_anchors(i);
            let mut ancposes = la.iter();
            let mut next_ancpos = ancposes.next();
            // For each character.
            for j in 0..wrap.width {
                // Send anchor.
                loop {
                    if let Some(&(a_id, p)) = next_ancpos {
                        if p.offset == j {
                            v.push(Display {
                                x: j,
                                y: i,
                                symbol: Symbol::Anchor(Anchor { id: *a_id }),
                                // symbol: Symbol::Void,
                            });
                            next_ancpos = ancposes.next();
                        } else {
                            break;
                        }
                    } else {
                        break;
                    }
                }
                // TODO anchor off the end.

                // Send character.
                let mut s = Symbol::Void;
                // TODO anchors
                if charsleft {
                    if let Some(c) = linetext.next() {
                        s = Symbol::Char(c);
                    } else {
                        charsleft = false;
                    }
                }
                v.push(Display {
                    x: j,
                    y: i,
                    symbol: s,
                });
            }
        }
        v
    }

    fn line_anchors(&self, i: i32) -> Vec<(&i64, &Position)> {
        self.anchors
            .iter()
            .filter_map(|id_p| match id_p.1.line == i {
                true => Some(id_p),
                false => None,
            })
            .collect()
    }

    pub fn anchor_at(&self, anchor: Anchor, x: i32, y: i32) -> bool {
        self.anchors.get(&anchor.id).map_or(false, |p| {
            let mx = (p.line as i32) == y;
            let my = (p.offset as i32) == x;
            mx && my
        })
    }
}

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut f = try!(fs::File::open(path));
    let mut contents = String::new();
    try!(f.read_to_string(&mut contents));
    Ok(contents)
}

fn canonicalize_move(mov: &Command) -> Command {
    match mov.clone() {
        Command::MoveLeft(n) => Command::MoveRight(-n),
        Command::MoveUp(n) => Command::MoveDown(-n),
        m @ _ => m,
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::StrAsciiExt;
use extra::arc::Arc;
use extra::sort::tim_sort;

use selectors::*;
use stylesheets::{Stylesheet, iter_style_rules};
use media_queries::{Device, Screen};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike};

pub enum StylesheetOrigin {
    UserAgentOrigin,
    AuthorOrigin,
    UserOrigin,
}

pub struct Stylist {
    priv ua_rules: PerOriginRules,
    priv author_rules: PerOriginRules,
    priv user_rules: PerOriginRules,
}

impl Stylist {
    #[inline]
    pub fn new() -> Stylist {
        Stylist {
            ua_rules: PerOriginRules::new(),
            author_rules: PerOriginRules::new(),
            user_rules: PerOriginRules::new(),
        }
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) {
        let rules = match origin {
            UserAgentOrigin => &mut self.ua_rules,
            AuthorOrigin => &mut self.author_rules,
            UserOrigin => &mut self.user_rules,
        };
        let mut added_normal_declarations = false;
        let mut added_important_declarations = false;

        macro_rules! append(
            ($priority: ident, $flag: ident) => {
                if style_rule.declarations.$priority.len() > 0 {
                    $flag = true;
                    for selector in style_rule.selectors.iter() {
                        // TODO: avoid copying?
                        rules.$priority.push(Rule {
                            selector: @(*selector).clone(),
                            declarations: Arc::new(style_rule.declarations.$priority.clone()),
                        })
                    }
                }
            };
        )

        let device = &Device { media_type: Screen };  // TODO, use Print when printing
        do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
            append!(normal, added_normal_declarations);
            append!(important, added_important_declarations);
        }

        // These sorts need to be stable
        // Do not sort already-sorted unchanged vectors
        if added_normal_declarations {
            tim_sort(rules.normal)
        }
        if added_important_declarations {
            tim_sort(rules.important)
        }
    }

    pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
            &self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
            pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
        assert!(element.is_element())
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements")
        let mut applicable_declarations = ~[];  // TODO: use an iterator?

        macro_rules! append(
            ($rules: expr) => {
                for rule in $rules.iter() {
                    if matches_selector::<N, T, E>(rule.selector, element, pseudo_element) {
                        applicable_declarations.push(rule.declarations.clone())
                    }
                }
            };
        );

        // In cascading order
        append!(self.ua_rules.normal);
        append!(self.user_rules.normal);

        // Style attributes have author origin but higher specificity than style rules.
        append!(self.author_rules.normal);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.normal.clone())));

        append!(self.author_rules.important);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.important.clone())));

        append!(self.user_rules.important);
        append!(self.ua_rules.important);

        applicable_declarations
    }
}

struct PerOriginRules {
    normal: ~[Rule],
    important: ~[Rule],
}

impl PerOriginRules {
    #[inline]
    fn new() -> PerOriginRules {
        PerOriginRules { normal: ~[], important: ~[] }
    }
}

#[deriving(Clone)]
struct Rule {
    selector: @Selector,
    declarations: Arc<~[PropertyDeclaration]>,
}

impl Ord for Rule {
    #[inline]
    fn lt(&self, other: &Rule) -> bool {
        self.selector.specificity < other.selector.specificity
    }
}

#[inline]
fn matches_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &Selector, element: &T, pseudo_element: Option<PseudoElement>) -> bool {
    selector.pseudo_element == pseudo_element &&
    matches_compound_selector::<N, T, E>(&selector.compound_selectors, element)
}

fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &CompoundSelector, element: &T) -> bool {
    if !do selector.simple_selectors.iter().all |simple_selector| {
            matches_simple_selector(simple_selector, element)
    } {
        return false
    }
    match selector.next {
        None => true,
        Some((ref next_selector, combinator)) => {
            let (siblings, just_one) = match combinator {
                Child => (false, true),
                Descendant => (false, false),
                NextSibling => (true, true),
                LaterSibling => (true, false),
            };
            let mut node = element.clone();
            loop {
                let next_node = if siblings {
                    node.node().prev_sibling()
                } else {
                    node.node().parent_node()
                };
                match next_node {
                    None => return false,
                    Some(next_node) => node = next_node,
                }
                if node.is_element() {
                    if matches_compound_selector(&**next_selector, &node) {
                        return true
                    } else if just_one {
                        return false
                    }
                }
            }
        }
    }
}

#[inline]
fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &SimpleSelector, element: &T) -> bool {
    static WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];

    match *selector {
        // TODO: case-sensitivity depends on the document type
        // TODO: intern element names
        LocalNameSelector(ref name) => {
            do element.with_imm_element_like |element: &E| {
                element.get_local_name().eq_ignore_ascii_case(name.as_slice())
            }
        }
        NamespaceSelector(_) => false,  // TODO, when the DOM supports namespaces on elements.
        // TODO: case-sensitivity depends on the document type and quirks mode
        // TODO: cache and intern IDs on elements.
        IDSelector(ref id) => {
            do element.with_imm_element_like |element: &E| {
                element.get_attr("id") == Some(id.as_slice())
            }
        }
        // TODO: cache and intern classe names on elements.
        ClassSelector(ref class) => {
            do element.with_imm_element_like |element: &E| {
                match element.get_attr("class") {
                    None => false,
                    // TODO: case-sensitivity depends on the document type and quirks mode
                    Some(ref class_attr)
                    => class_attr.split_iter(WHITESPACE).any(|c| c == class.as_slice()),
                }
            }
        }

        AttrExists(ref attr) => match_attribute(attr, element, |_| true),
        AttrEqual(ref attr, ref value) => match_attribute(attr, element, |v| v == value.as_slice()),
        AttrIncludes(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.split_iter(WHITESPACE).any(|v| v == value.as_slice())
        },
        AttrDashMatch(ref attr, ref value, ref dashing_value)
        => do match_attribute(attr, element) |attr_value| {
            attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
        },
        AttrPrefixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.starts_with(value.as_slice())
        },
        AttrSubstringMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.contains(value.as_slice())
        },
        AttrSuffixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.ends_with(value.as_slice())
        },

        FirstChild => matches_first_child(element),

        Negation(ref negated) => {
            !negated.iter().all(|s| matches_simple_selector(s, element))
        },
    }
}

#[inline]
fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        element: &T) -> bool {
    let mut node = element.clone();
    loop {
        match node.node().prev_sibling() {
            Some(prev_sibling) => {
                node = prev_sibling;
                if node.is_element() {
                    return false
                }
            }
            None => return !element.is_root(),
        }
    }
}

#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
    do element.with_imm_element_like |element: &E| {
        match attr.namespace {
            Some(_) => false,  // TODO, when the DOM supports namespaces on attributes
            None => match element.get_attr(attr.name) {
                None => false,
                Some(ref value) => f(value.as_slice())
            }
        }
    }
}
<commit_msg>Remove some at-pointers that are not actually needed. (Selector matching)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ascii::StrAsciiExt;
use extra::arc::Arc;
use extra::sort::tim_sort;

use selectors::*;
use stylesheets::{Stylesheet, iter_style_rules};
use media_queries::{Device, Screen};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike};

pub enum StylesheetOrigin {
    UserAgentOrigin,
    AuthorOrigin,
    UserOrigin,
}

pub struct Stylist {
    priv ua_rules: PerOriginRules,
    priv author_rules: PerOriginRules,
    priv user_rules: PerOriginRules,
}

impl Stylist {
    #[inline]
    pub fn new() -> Stylist {
        Stylist {
            ua_rules: PerOriginRules::new(),
            author_rules: PerOriginRules::new(),
            user_rules: PerOriginRules::new(),
        }
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet, origin: StylesheetOrigin) {
        let rules = match origin {
            UserAgentOrigin => &mut self.ua_rules,
            AuthorOrigin => &mut self.author_rules,
            UserOrigin => &mut self.user_rules,
        };
        let mut added_normal_declarations = false;
        let mut added_important_declarations = false;

        macro_rules! append(
            ($priority: ident, $flag: ident) => {
                if style_rule.declarations.$priority.len() > 0 {
                    $flag = true;
                    for selector in style_rule.selectors.iter() {
                        // TODO: avoid copying?
                        rules.$priority.push(Rule {
                            selector: selector.clone(),
                            declarations: Arc::new(style_rule.declarations.$priority.clone()),
                        })
                    }
                }
            };
        )

        let device = &Device { media_type: Screen };  // TODO, use Print when printing
        do iter_style_rules(stylesheet.rules.as_slice(), device) |style_rule| {
            append!(normal, added_normal_declarations);
            append!(important, added_important_declarations);
        }

        // These sorts need to be stable
        // Do not sort already-sorted unchanged vectors
        if added_normal_declarations {
            tim_sort(rules.normal)
        }
        if added_important_declarations {
            tim_sort(rules.important)
        }
    }

    pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
            &self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
            pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
        assert!(element.is_element())
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements")
        let mut applicable_declarations = ~[];  // TODO: use an iterator?

        macro_rules! append(
            ($rules: expr) => {
                for rule in $rules.iter() {
                    if matches_selector::<N, T, E>(&rule.selector, element, pseudo_element) {
                        applicable_declarations.push(rule.declarations.clone())
                    }
                }
            };
        );

        // In cascading order
        append!(self.ua_rules.normal);
        append!(self.user_rules.normal);

        // Style attributes have author origin but higher specificity than style rules.
        append!(self.author_rules.normal);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.normal.clone())));

        append!(self.author_rules.important);
        // TODO: avoid copying?
        style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.important.clone())));

        append!(self.user_rules.important);
        append!(self.ua_rules.important);

        applicable_declarations
    }
}

struct PerOriginRules {
    normal: ~[Rule],
    important: ~[Rule],
}

impl PerOriginRules {
    #[inline]
    fn new() -> PerOriginRules {
        PerOriginRules { normal: ~[], important: ~[] }
    }
}

#[deriving(Clone)]
struct Rule {
    selector: Selector,
    declarations: Arc<~[PropertyDeclaration]>,
}

impl Ord for Rule {
    #[inline]
    fn lt(&self, other: &Rule) -> bool {
        self.selector.specificity < other.selector.specificity
    }
}

#[inline]
fn matches_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &Selector, element: &T, pseudo_element: Option<PseudoElement>) -> bool {
    selector.pseudo_element == pseudo_element &&
    matches_compound_selector::<N, T, E>(&selector.compound_selectors, element)
}

fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &CompoundSelector, element: &T) -> bool {
    if !do selector.simple_selectors.iter().all |simple_selector| {
            matches_simple_selector(simple_selector, element)
    } {
        return false
    }
    match selector.next {
        None => true,
        Some((ref next_selector, combinator)) => {
            let (siblings, just_one) = match combinator {
                Child => (false, true),
                Descendant => (false, false),
                NextSibling => (true, true),
                LaterSibling => (true, false),
            };
            let mut node = element.clone();
            loop {
                let next_node = if siblings {
                    node.node().prev_sibling()
                } else {
                    node.node().parent_node()
                };
                match next_node {
                    None => return false,
                    Some(next_node) => node = next_node,
                }
                if node.is_element() {
                    if matches_compound_selector(&**next_selector, &node) {
                        return true
                    } else if just_one {
                        return false
                    }
                }
            }
        }
    }
}

#[inline]
fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        selector: &SimpleSelector, element: &T) -> bool {
    static WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];

    match *selector {
        // TODO: case-sensitivity depends on the document type
        // TODO: intern element names
        LocalNameSelector(ref name) => {
            do element.with_imm_element_like |element: &E| {
                element.get_local_name().eq_ignore_ascii_case(name.as_slice())
            }
        }
        NamespaceSelector(_) => false,  // TODO, when the DOM supports namespaces on elements.
        // TODO: case-sensitivity depends on the document type and quirks mode
        // TODO: cache and intern IDs on elements.
        IDSelector(ref id) => {
            do element.with_imm_element_like |element: &E| {
                element.get_attr("id") == Some(id.as_slice())
            }
        }
        // TODO: cache and intern classe names on elements.
        ClassSelector(ref class) => {
            do element.with_imm_element_like |element: &E| {
                match element.get_attr("class") {
                    None => false,
                    // TODO: case-sensitivity depends on the document type and quirks mode
                    Some(ref class_attr)
                    => class_attr.split_iter(WHITESPACE).any(|c| c == class.as_slice()),
                }
            }
        }

        AttrExists(ref attr) => match_attribute(attr, element, |_| true),
        AttrEqual(ref attr, ref value) => match_attribute(attr, element, |v| v == value.as_slice()),
        AttrIncludes(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.split_iter(WHITESPACE).any(|v| v == value.as_slice())
        },
        AttrDashMatch(ref attr, ref value, ref dashing_value)
        => do match_attribute(attr, element) |attr_value| {
            attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
        },
        AttrPrefixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.starts_with(value.as_slice())
        },
        AttrSubstringMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.contains(value.as_slice())
        },
        AttrSuffixMatch(ref attr, ref value) => do match_attribute(attr, element) |attr_value| {
            attr_value.ends_with(value.as_slice())
        },

        FirstChild => matches_first_child(element),

        Negation(ref negated) => {
            !negated.iter().all(|s| matches_simple_selector(s, element))
        },
    }
}

#[inline]
fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        element: &T) -> bool {
    let mut node = element.clone();
    loop {
        match node.node().prev_sibling() {
            Some(prev_sibling) => {
                node = prev_sibling;
                if node.is_element() {
                    return false
                }
            }
            None => return !element.is_root(),
        }
    }
}

#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
        attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
    do element.with_imm_element_like |element: &E| {
        match attr.namespace {
            Some(_) => false,  // TODO, when the DOM supports namespaces on attributes
            None => match element.get_attr(attr.name) {
                None => false,
                Some(ref value) => f(value.as_slice())
            }
        }
    }
}
<|endoftext|>


<commit_before>#![crate_id(name = "protobuf#0.1.0")]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]

#![desc = "protobuf implementation for rust"]
#![license = "BSD"]

extern crate sync;
extern crate collections;
extern crate debug;

pub use unknown::UnknownFields;
pub use unknown::UnknownValues;
pub use unknown::UnknownValue;
pub use unknown::UnknownValueRef;
pub use unknown::UnknownValuesIter;
pub use unknown::UnknownFieldIter;
pub use repeated::RepeatedField;
pub use singular::SingularField;
pub use singular::SingularPtrField;
pub use clear::Clear;
pub use core::CodedInputStream;
pub use core::CodedOutputStream;
pub use core::Message;
pub use core::parse_from_bytes;
pub use core::parse_from_reader;
pub use core::parse_length_delimited_from;
pub use core::wire_format;

pub mod core;
pub mod rt;
pub mod lazy;
pub mod descriptor;
pub mod codegen;
pub mod repeated;
pub mod singular;
pub mod clear;
pub mod reflect;
pub mod text_format;

mod misc;
mod zigzag;
mod hex;
mod paginate;
mod unknown;
mod strx;
mod descriptorx;

#[cfg(test)]
mod shrug;
#[cfg(test)]
mod test_root;
#[cfg(test)]
mod test;
#[cfg(test)]
mod text_format_test_data;
#[cfg(test)]
mod test_nonunique_enum;

// so `use protobuf::*` could work in descriptor mod
pub mod protobuf {
    pub use descriptor;
    pub use codegen;
    pub use reflect;
    pub use core::*;
    pub use rt;
    pub use lazy;
    pub use unknown::UnknownFields;
    pub use unknown::UnknownValues;
    pub use unknown::UnknownValue;
    pub use unknown::UnknownValueRef;
    pub use unknown::UnknownValuesIter;
    pub use unknown::UnknownFieldIter;
    pub use repeated::RepeatedField;
    pub use singular::SingularField;
    pub use singular::SingularPtrField;
    pub use clear::Clear;
}
<commit_msg>Expose enums<commit_after>#![crate_id(name = "protobuf#0.1.0")]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]

#![desc = "protobuf implementation for rust"]
#![license = "BSD"]

extern crate sync;
extern crate collections;
extern crate debug;

pub use unknown::UnknownFields;
pub use unknown::UnknownValues;
pub use unknown::UnknownValue;
pub use unknown::UnknownValueRef;
pub use unknown::UnknownValuesIter;
pub use unknown::UnknownFieldIter;
pub use repeated::RepeatedField;
pub use singular::SingularField;
pub use singular::SingularPtrField;
pub use clear::Clear;
pub use core::CodedInputStream;
pub use core::CodedOutputStream;
pub use core::Message;
pub use core::ProtobufEnum;
pub use core::parse_from_bytes;
pub use core::parse_from_reader;
pub use core::parse_length_delimited_from;
pub use core::wire_format;

pub mod core;
pub mod rt;
pub mod lazy;
pub mod descriptor;
pub mod codegen;
pub mod repeated;
pub mod singular;
pub mod clear;
pub mod reflect;
pub mod text_format;

mod misc;
mod zigzag;
mod hex;
mod paginate;
mod unknown;
mod strx;
mod descriptorx;

#[cfg(test)]
mod shrug;
#[cfg(test)]
mod test_root;
#[cfg(test)]
mod test;
#[cfg(test)]
mod text_format_test_data;
#[cfg(test)]
mod test_nonunique_enum;

// so `use protobuf::*` could work in descriptor mod
pub mod protobuf {
    pub use descriptor;
    pub use codegen;
    pub use reflect;
    pub use core::*;
    pub use rt;
    pub use lazy;
    pub use unknown::UnknownFields;
    pub use unknown::UnknownValues;
    pub use unknown::UnknownValue;
    pub use unknown::UnknownValueRef;
    pub use unknown::UnknownValuesIter;
    pub use unknown::UnknownFieldIter;
    pub use repeated::RepeatedField;
    pub use singular::SingularField;
    pub use singular::SingularPtrField;
    pub use clear::Clear;
}
<|endoftext|>


<commit_before>use futures_core::task::{Waker, RawWaker, RawWakerVTable};
use core::ptr::null;
use once_cell::sync::Lazy;

unsafe fn clone_panic_waker(_data: *const ()) -> RawWaker {
    raw_panic_waker()
}

unsafe fn noop(_data: *const ()) {}

unsafe fn wake_panic(_data: *const ()) {
    panic!("should not be woken");
}

const PANIC_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
    clone_panic_waker,
    wake_panic,
    wake_panic,
    noop,
);

fn raw_panic_waker() -> RawWaker {
    RawWaker::new(null(), &PANIC_WAKER_VTABLE)
}

/// Create a new [`Waker`](futures_core::task::Waker) which will
/// panic when `wake()` is called on it. The [`Waker`] can be converted
/// into a [`Waker`] which will behave the same way.
///
/// # Examples
///
/// ```should_panic
/// use futures_test::task::panic_waker;
///
/// let waker = panic_waker();
/// waker.wake(); // Will panic
/// ```
pub fn panic_waker() -> Waker {
    unsafe { Waker::from_raw(raw_panic_waker()) }
}

/// Get a global reference to a
/// [`Waker`](futures_core::task::Waker) referencing a singleton
/// instance of a [`Waker`] which panics when woken.
///
/// # Examples
///
/// ```should_panic
/// use futures_test::task::panic_waker_ref;
///
/// let waker = panic_waker_ref();
/// waker.wake_by_ref(); // Will panic
/// ```
pub fn panic_waker_ref() -> &'static Waker {
    static PANIC_WAKER_INSTANCE: Lazy<Waker> = Lazy::new(panic_waker);
    &*PANIC_WAKER_INSTANCE
}

#[cfg(test)]
mod tests {
    #[test]
    #[should_panic(expected = "should not be woken")]
    fn issue_2091_cross_thread_segfault() {
        let waker = std::thread::spawn(super::panic_waker_ref).join().unwrap();
        waker.wake_by_ref();
    }
}
<commit_msg>Don't double panic in futures-test::test::panic_waker::wake_panic (#2236)<commit_after>use futures_core::task::{Waker, RawWaker, RawWakerVTable};
use core::ptr::null;
use once_cell::sync::Lazy;

unsafe fn clone_panic_waker(_data: *const ()) -> RawWaker {
    raw_panic_waker()
}

unsafe fn noop(_data: *const ()) {}

unsafe fn wake_panic(_data: *const ()) {
    if ! std::thread::panicking() {
        panic!("should not be woken");
    }
}

const PANIC_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
    clone_panic_waker,
    wake_panic,
    wake_panic,
    noop,
);

fn raw_panic_waker() -> RawWaker {
    RawWaker::new(null(), &PANIC_WAKER_VTABLE)
}

/// Create a new [`Waker`](futures_core::task::Waker) which will
/// panic when `wake()` is called on it. The [`Waker`] can be converted
/// into a [`Waker`] which will behave the same way.
///
/// # Examples
///
/// ```should_panic
/// use futures_test::task::panic_waker;
///
/// let waker = panic_waker();
/// waker.wake(); // Will panic
/// ```
pub fn panic_waker() -> Waker {
    unsafe { Waker::from_raw(raw_panic_waker()) }
}

/// Get a global reference to a
/// [`Waker`](futures_core::task::Waker) referencing a singleton
/// instance of a [`Waker`] which panics when woken.
///
/// # Examples
///
/// ```should_panic
/// use futures_test::task::panic_waker_ref;
///
/// let waker = panic_waker_ref();
/// waker.wake_by_ref(); // Will panic
/// ```
pub fn panic_waker_ref() -> &'static Waker {
    static PANIC_WAKER_INSTANCE: Lazy<Waker> = Lazy::new(panic_waker);
    &*PANIC_WAKER_INSTANCE
}

#[cfg(test)]
mod tests {
    #[test]
    #[should_panic(expected = "should not be woken")]
    fn issue_2091_cross_thread_segfault() {
        let waker = std::thread::spawn(super::panic_waker_ref).join().unwrap();
        waker.wake_by_ref();
    }
}
<|endoftext|>