<commit_before>use std::collections::btree_map::{BTreeMap, Range};
use std::cmp::Ordering::*;
use std::iter::Peekable;

use super::Result;

use self::NextIterValue::*;

pub type Patch = BTreeMap<Vec<u8>, Change>;
pub type Iter<'a> = Box<Iterator<'a> + 'a>;

#[derive(Debug, Clone)]
pub enum Change {
    Put(Vec<u8>),
    Delete,
}

// FIXME: make &mut Fork "unwind safe"
pub struct Fork {
    snapshot: Box<Snapshot>,
    changes: Patch,
    changelog: Vec<(Vec<u8>, Option<Change>)>,
    logged: bool
}

pub struct ForkIter<'a> {
    snapshot: Iter<'a>,
    changes: Peekable<Range<'a, Vec<u8>, Change>>,
}

#[derive(Debug, PartialEq, Eq)]
enum NextIterValue {
    Stored,
    Replaced,
    Inserted,
    Deleted,
    MissDeleted,
    Finished
}

pub trait Database: Send + Sync + 'static {
    fn clone(&self) -> Box<Database>;
    fn snapshot(&self) -> Box<Snapshot>;
    fn fork(&self) -> Fork {
        Fork {
            snapshot: self.snapshot(),
            changes: Patch::new(),
            changelog: Vec::new(),
            logged: false,
        }
    }
    fn merge(&mut self, patch: Patch) -> Result<()>;
}

pub trait Snapshot {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
    fn contains(&self, key: &[u8]) -> bool {
        self.get(key).is_some()
    }
    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a>;
}

pub trait Iterator<'a> {
    fn next(&mut self) -> Option<(&[u8], &[u8])>;
    fn peek(&mut self) -> Option<(&[u8], &[u8])>;
}

impl Snapshot for Fork {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        match self.changes.get(key) {
            Some(change) => {
                match *change {
                    Change::Put(ref v) => Some(v.clone()),
                    Change::Delete => None,
                }
            }
            None => self.snapshot.get(key),
        }
    }

    fn contains(&self, key: &[u8]) -> bool {
        match self.changes.get(key) {
            Some(change) => {
                match *change {
                    Change::Put(..) => true,
                    Change::Delete => false,
                }
            }
            None => self.snapshot.get(key).is_some(),
        }
    }

    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a> {
        use std::collections::Bound::*;
        let range = (Included(from), Unbounded);
        Box::new(ForkIter {
                     snapshot: self.snapshot.iter(from),
                     changes: self.changes.range::<[u8], _>(range).peekable(),
                 })
    }
}

impl Fork {
    pub fn checkpoint(&mut self) {
        if self.logged {
            panic!("call checkpoint before rollback or commit");
        }
        self.logged = true;
    }

    pub fn commit(&mut self) {
        if !self.logged {
            panic!("call commit before checkpoint");
        }
        self.changelog.clear();
        self.logged = false;
    }

    pub fn rollback(&mut self) {
        if !self.logged {
            panic!("call rollback before checkpoint");
        }
        for (k, c) in self.changelog.drain(..).rev() {
            match c {
                Some(change) => self.changes.insert(k, change),
                None => self.changes.remove(&k),
            };
        }
        self.logged = false;
    }

    pub fn put(&mut self, key: Vec<u8>, value: Vec<u8>) {
        if self.logged {
            self.changelog.push((key.clone(), self.changes.insert(key, Change::Put(value))));
        } else {
            self.changes.insert(key, Change::Put(value));
        }
    }

    pub fn remove(&mut self, key: Vec<u8>) {
        if self.logged {
            self.changelog.push((key.clone(), self.changes.insert(key, Change::Delete)));
        } else {
            self.changes.insert(key, Change::Delete);
        }
    }

    pub fn remove_by_prefix(&mut self, prefix: &[u8]) {
        let mut iter = self.snapshot.iter(prefix);
        while let Some((k, ..)) = iter.next() {
            if !k.starts_with(prefix) {
                return;
            }
            let change = self.changes.insert(k.to_vec(), Change::Delete);
            if self.logged {
                self.changelog.push((k.to_vec(), change));
            }
        }
    }

    pub fn into_patch(self) -> Patch {
        self.changes
    }

    pub fn merge(&mut self, patch: Patch) {
        if self.logged {
            panic!("call merge before commit or rollback");
        }
        self.changes.extend(patch)
    }
}

// TODO: Does we needed this AsRef / AsMut impls?

impl AsRef<Snapshot> for Snapshot {
    fn as_ref<'a>(&'a self) -> &'a (Snapshot + 'static) {
        self
    }
}

impl AsRef<Snapshot + 'static> for Fork {
    fn as_ref<'a>(&'a self) -> &'a (Snapshot + 'static) {
        &*self
    }
}

impl ::std::fmt::Debug for Fork {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "Fork(..)")
    }
}

impl<'a> ForkIter<'a> {
    fn step(&mut self) -> NextIterValue {
        match self.changes.peek() {
            Some(&(k, ref change)) => {
                match self.snapshot.peek() {
                    Some((key, ..)) => {
                        match **change {
                            Change::Put(..) => {
                                match k[..].cmp(key) {
                                    Equal => Replaced,
                                    Less => Inserted,
                                    Greater => Stored,
                                }
                            }
                            Change::Delete => {
                                match k[..].cmp(key) {
                                    Equal => Deleted,
                                    Less => MissDeleted,
                                    Greater => Stored,
                                }
                            }
                        }
                    }
                    None => {
                        match **change {
                            Change::Put(..) => Inserted,
                            Change::Delete => MissDeleted,
                        }
                    }
                }
            }
            None => {
                match self.snapshot.peek() {
                    Some(..) => Stored,
                    None => Finished,
                }
            }
        }
    }
}

impl<'a> Iterator<'a> for ForkIter<'a> {
    fn next(&mut self) -> Option<(&[u8], &[u8])> {
        loop {
            match self.step() {
                Stored => {
                    return self.snapshot.next()
                },
                Replaced => {
                    self.snapshot.next();
                    return self.changes.next()
                                       .map(|(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Inserted => {
                    return self.changes.next()
                                       .map(|(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Deleted => {
                    self.changes.next();
                    self.snapshot.next();
                },
                MissDeleted => {
                    self.changes.next();
                },
                Finished => return None,
            }
        }
    }

    fn peek(&mut self) -> Option<(&[u8], &[u8])> {
        loop {
            match self.step() {
                Stored | Replaced => {
                    return self.snapshot.peek()
                },
                Inserted => {
                    return self.changes.peek()
                                       .map(|&(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Deleted => {
                    self.changes.next();
                    self.snapshot.next();
                },
                MissDeleted => {
                    self.changes.next();
                },
                Finished => return None,
            }
        }
    }
}
<commit_msg>Fix AsRef impl lifetimes for Snapshot and Fork<commit_after>use std::collections::btree_map::{BTreeMap, Range};
use std::cmp::Ordering::*;
use std::iter::Peekable;

use super::Result;

use self::NextIterValue::*;

pub type Patch = BTreeMap<Vec<u8>, Change>;
pub type Iter<'a> = Box<Iterator<'a> + 'a>;

#[derive(Debug, Clone)]
pub enum Change {
    Put(Vec<u8>),
    Delete,
}

// FIXME: make &mut Fork "unwind safe"
pub struct Fork {
    snapshot: Box<Snapshot>,
    changes: Patch,
    changelog: Vec<(Vec<u8>, Option<Change>)>,
    logged: bool
}

pub struct ForkIter<'a> {
    snapshot: Iter<'a>,
    changes: Peekable<Range<'a, Vec<u8>, Change>>,
}

#[derive(Debug, PartialEq, Eq)]
enum NextIterValue {
    Stored,
    Replaced,
    Inserted,
    Deleted,
    MissDeleted,
    Finished
}

pub trait Database: Send + Sync + 'static {
    fn clone(&self) -> Box<Database>;
    fn snapshot(&self) -> Box<Snapshot>;
    fn fork(&self) -> Fork {
        Fork {
            snapshot: self.snapshot(),
            changes: Patch::new(),
            changelog: Vec::new(),
            logged: false,
        }
    }
    fn merge(&mut self, patch: Patch) -> Result<()>;
}

pub trait Snapshot {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
    fn contains(&self, key: &[u8]) -> bool {
        self.get(key).is_some()
    }
    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a>;
}

pub trait Iterator<'a> {
    fn next(&mut self) -> Option<(&[u8], &[u8])>;
    fn peek(&mut self) -> Option<(&[u8], &[u8])>;
}

impl Snapshot for Fork {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        match self.changes.get(key) {
            Some(change) => {
                match *change {
                    Change::Put(ref v) => Some(v.clone()),
                    Change::Delete => None,
                }
            }
            None => self.snapshot.get(key),
        }
    }

    fn contains(&self, key: &[u8]) -> bool {
        match self.changes.get(key) {
            Some(change) => {
                match *change {
                    Change::Put(..) => true,
                    Change::Delete => false,
                }
            }
            None => self.snapshot.get(key).is_some(),
        }
    }

    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a> {
        use std::collections::Bound::*;
        let range = (Included(from), Unbounded);
        Box::new(ForkIter {
                     snapshot: self.snapshot.iter(from),
                     changes: self.changes.range::<[u8], _>(range).peekable(),
                 })
    }
}

impl Fork {
    pub fn checkpoint(&mut self) {
        if self.logged {
            panic!("call checkpoint before rollback or commit");
        }
        self.logged = true;
    }

    pub fn commit(&mut self) {
        if !self.logged {
            panic!("call commit before checkpoint");
        }
        self.changelog.clear();
        self.logged = false;
    }

    pub fn rollback(&mut self) {
        if !self.logged {
            panic!("call rollback before checkpoint");
        }
        for (k, c) in self.changelog.drain(..).rev() {
            match c {
                Some(change) => self.changes.insert(k, change),
                None => self.changes.remove(&k),
            };
        }
        self.logged = false;
    }

    pub fn put(&mut self, key: Vec<u8>, value: Vec<u8>) {
        if self.logged {
            self.changelog.push((key.clone(), self.changes.insert(key, Change::Put(value))));
        } else {
            self.changes.insert(key, Change::Put(value));
        }
    }

    pub fn remove(&mut self, key: Vec<u8>) {
        if self.logged {
            self.changelog.push((key.clone(), self.changes.insert(key, Change::Delete)));
        } else {
            self.changes.insert(key, Change::Delete);
        }
    }

    pub fn remove_by_prefix(&mut self, prefix: &[u8]) {
        let mut iter = self.snapshot.iter(prefix);
        while let Some((k, ..)) = iter.next() {
            if !k.starts_with(prefix) {
                return;
            }
            let change = self.changes.insert(k.to_vec(), Change::Delete);
            if self.logged {
                self.changelog.push((k.to_vec(), change));
            }
        }
    }

    pub fn into_patch(self) -> Patch {
        self.changes
    }

    pub fn merge(&mut self, patch: Patch) {
        if self.logged {
            panic!("call merge before commit or rollback");
        }
        self.changes.extend(patch)
    }
}

// TODO: Does we needed this AsRef / AsMut impls?

impl<'b> AsRef<Snapshot + 'b> for Snapshot {
    fn as_ref<'a>(&'a self) -> &'a (Snapshot + 'b) {
        self
    }
}

impl<'b> AsRef<Snapshot + 'b> for Fork {
    fn as_ref<'a>(&'a self) -> &'a (Snapshot + 'b) {
        &*self
    }
}

impl ::std::fmt::Debug for Fork {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "Fork(..)")
    }
}

impl<'a> ForkIter<'a> {
    fn step(&mut self) -> NextIterValue {
        match self.changes.peek() {
            Some(&(k, ref change)) => {
                match self.snapshot.peek() {
                    Some((key, ..)) => {
                        match **change {
                            Change::Put(..) => {
                                match k[..].cmp(key) {
                                    Equal => Replaced,
                                    Less => Inserted,
                                    Greater => Stored,
                                }
                            }
                            Change::Delete => {
                                match k[..].cmp(key) {
                                    Equal => Deleted,
                                    Less => MissDeleted,
                                    Greater => Stored,
                                }
                            }
                        }
                    }
                    None => {
                        match **change {
                            Change::Put(..) => Inserted,
                            Change::Delete => MissDeleted,
                        }
                    }
                }
            }
            None => {
                match self.snapshot.peek() {
                    Some(..) => Stored,
                    None => Finished,
                }
            }
        }
    }
}

impl<'a> Iterator<'a> for ForkIter<'a> {
    fn next(&mut self) -> Option<(&[u8], &[u8])> {
        loop {
            match self.step() {
                Stored => {
                    return self.snapshot.next()
                },
                Replaced => {
                    self.snapshot.next();
                    return self.changes.next()
                                       .map(|(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Inserted => {
                    return self.changes.next()
                                       .map(|(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Deleted => {
                    self.changes.next();
                    self.snapshot.next();
                },
                MissDeleted => {
                    self.changes.next();
                },
                Finished => return None,
            }
        }
    }

    fn peek(&mut self) -> Option<(&[u8], &[u8])> {
        loop {
            match self.step() {
                Stored | Replaced => {
                    return self.snapshot.peek()
                },
                Inserted => {
                    return self.changes.peek()
                                       .map(|&(key, change)| (key.as_slice(), match *change {
                                            Change::Put(ref value) => value.as_slice(),
                                            Change::Delete => unreachable!()
                                       }))
                },
                Deleted => {
                    self.changes.next();
                    self.snapshot.next();
                },
                MissDeleted => {
                    self.changes.next();
                },
                Finished => return None,
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    api_utils::{
        create_command, get, get_all, get_hosts, get_one, wait_for_cmds, SendCmd, SendJob,
    },
    display_utils::{generate_table, wrap_fut},
    error::ImlManagerCliError,
    ostpool::{ostpool_cli, OstPoolCommand},
};
use futures::future::{try_join, try_join_all};
use iml_wire_types::{ApiList, Filesystem, FlatQuery, Mgt, Ost};
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use std::collections::BTreeMap;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
pub enum FilesystemCommand {
    /// List all configured filesystems
    #[structopt(name = "list")]
    List,
    /// Show filesystem
    #[structopt(name = "show")]
    Show {
        #[structopt(name = "FSNAME")]
        fsname: String,
    },
    /// Ost Pools
    #[structopt(name = "pool")]
    Pool {
        #[structopt(subcommand)]
        command: OstPoolCommand,
    },
    /// Detect existing filesystem
    #[structopt(name = "detect")]
    Detect {
        #[structopt(short, long)]
        hosts: Option<String>,
    },
}

#[derive(serde::Serialize)]
pub struct HostList {
    hosts: Vec<String>,
}

fn usage(
    free: Option<f64>,
    total: Option<f64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total - free, Some(0)),
            formatter(total, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

async fn detect_filesystem(hosts: Option<String>) -> Result<(), ImlManagerCliError> {
    let hosts = if let Some(hl) = hosts {
        let hostlist = hostlist_parser::parse(&hl)?;
        tracing::debug!("Host Names: {:?}", hostlist);
        let all_hosts = get_hosts().await?;

        let hostmap: BTreeMap<&str, &str> = all_hosts
            .objects
            .iter()
            .map(|h| {
                vec![
                    (h.nodename.as_str(), h.resource_uri.as_str()),
                    (h.fqdn.as_str(), h.resource_uri.as_str()),
                ]
            })
            .flatten()
            .collect();

        hostlist
            .iter()
            .filter_map(|h| hostmap.get(h.as_str()))
            .map(|x| (*x).to_string())
            .collect()
    } else {
        vec![]
    };

    tracing::debug!("Host APIs: {:?}", hosts);

    let cmd = SendCmd {
        message: "Detecting filesystems".into(),
        jobs: vec![SendJob {
            class_name: "DetectTargetsJob".into(),
            args: HostList { hosts },
        }],
    };
    let cmd = wrap_fut("Detecting filesystems...", create_command(cmd)).await?;

    wait_for_cmds(vec![cmd]).await?;
    Ok(())
}

pub async fn filesystem_cli(command: FilesystemCommand) -> Result<(), ImlManagerCliError> {
    match command {
        FilesystemCommand::List => {
            let filesystems: ApiList<Filesystem> =
                wrap_fut("Fetching filesystems...", get_all()).await?;

            tracing::debug!("FSs: {:?}", filesystems);

            let table = generate_table(
                &[
                    "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
                ],
                filesystems.objects.into_iter().map(|f| {
                    vec![
                        f.label,
                        f.state,
                        usage(f.bytes_free, f.bytes_total, format_bytes),
                        usage(f.files_free, f.files_total, format_number),
                        format_number(f.client_count.unwrap_or(0.0), Some(0)),
                        f.mdts.len().to_string(),
                        f.osts.len().to_string(),
                    ]
                }),
            );

            table.printstd();
        }
        FilesystemCommand::Show { fsname } => {
            let fs: Filesystem =
                wrap_fut("Fetching filesystem...", get_one(vec![("name", &fsname)])).await?;

            tracing::debug!("FS: {:?}", fs);

            let (mgt, osts): (Mgt, Vec<Ost>) = try_join(
                wrap_fut("Fetching MGT...", get(&fs.mgt, Mgt::query())),
                try_join_all(fs.osts.into_iter().map(|o| async move {
                    wrap_fut("Fetching OST...", get(&o, Ost::query())).await
                })),
            )
            .await?;

            let mut table = Table::new();
            table.add_row(Row::from(&["Name".to_string(), fs.label]));
            table.add_row(Row::from(&[
                "Space".to_string(),
                usage(fs.bytes_free, fs.bytes_total, format_bytes),
            ]));
            table.add_row(Row::from(&[
                "Inodes".to_string(),
                usage(fs.files_free, fs.files_total, format_number),
            ]));
            table.add_row(Row::from(&["State".to_string(), fs.state]));
            table.add_row(Row::from(&[
                "Management Server".to_string(),
                mgt.active_host_name,
            ]));

            let mdtnames: Vec<String> = fs.mdts.into_iter().map(|m| m.name).collect();
            table.add_row(Row::from(&["MDTs".to_string(), mdtnames.join("\n")]));

            let ostnames: Vec<String> = osts.into_iter().map(|m| m.name).collect();
            table.add_row(Row::from(&["OSTs".to_string(), ostnames.join("\n")]));

            table.add_row(Row::from(&[
                "Clients".to_string(),
                format!("{:0}", fs.client_count.unwrap_or(0.0)),
            ]));
            table.add_row(Row::from(&["Mount Path".to_string(), fs.mount_path]));
            table.printstd();
        }
        FilesystemCommand::Pool { command } => ostpool_cli(command).await?,
        FilesystemCommand::Detect { hosts } => detect_filesystem(hosts).await?,
    };

    Ok(())
}
<commit_msg>Only pass hosts argument to DetectTarget if there are hosts (#1631)<commit_after>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    api_utils::{
        create_command, get, get_all, get_hosts, get_one, wait_for_cmds, SendCmd, SendJob,
    },
    display_utils::{generate_table, wrap_fut},
    error::ImlManagerCliError,
    ostpool::{ostpool_cli, OstPoolCommand},
};
use futures::future::{try_join, try_join_all};
use iml_wire_types::{ApiList, Filesystem, FlatQuery, Mgt, Ost};
use number_formatter::{format_bytes, format_number};
use prettytable::{Row, Table};
use std::collections::{BTreeMap, HashMap};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
pub enum FilesystemCommand {
    /// List all configured filesystems
    #[structopt(name = "list")]
    List,
    /// Show filesystem
    #[structopt(name = "show")]
    Show {
        #[structopt(name = "FSNAME")]
        fsname: String,
    },
    /// Ost Pools
    #[structopt(name = "pool")]
    Pool {
        #[structopt(subcommand)]
        command: OstPoolCommand,
    },
    /// Detect existing filesystem
    #[structopt(name = "detect")]
    Detect {
        #[structopt(short, long)]
        hosts: Option<String>,
    },
}

fn usage(
    free: Option<f64>,
    total: Option<f64>,
    formatter: fn(f64, Option<usize>) -> String,
) -> String {
    match (free, total) {
        (Some(free), Some(total)) => format!(
            "{} / {}",
            formatter(total - free, Some(0)),
            formatter(total, Some(0))
        ),
        (None, Some(total)) => format!("Calculating ... / {}", formatter(total, Some(0))),
        _ => "Calculating ...".to_string(),
    }
}

async fn detect_filesystem(hosts: Option<String>) -> Result<(), ImlManagerCliError> {
    let hosts = if let Some(hl) = hosts {
        let hostlist = hostlist_parser::parse(&hl)?;
        tracing::debug!("Host Names: {:?}", hostlist);
        let all_hosts = get_hosts().await?;

        let hostmap: BTreeMap<&str, &str> = all_hosts
            .objects
            .iter()
            .map(|h| {
                vec![
                    (h.nodename.as_str(), h.resource_uri.as_str()),
                    (h.fqdn.as_str(), h.resource_uri.as_str()),
                ]
            })
            .flatten()
            .collect();

        hostlist
            .iter()
            .filter_map(|h| hostmap.get(h.as_str()))
            .map(|x| (*x).to_string())
            .collect()
    } else {
        vec![]
    };

    tracing::debug!("Host APIs: {:?}", hosts);

    let args = if hosts.is_empty() {
        vec![]
    } else {
        vec![("hosts".to_string(), hosts)]
    };

    let cmd = SendCmd {
        message: "Detecting filesystems".into(),
        jobs: vec![SendJob::<HashMap<String, Vec<String>>> {
            class_name: "DetectTargetsJob".into(),
            args: args.into_iter().collect(),
        }],
    };
    let cmd = wrap_fut("Detecting filesystems...", create_command(cmd)).await?;

    wait_for_cmds(vec![cmd]).await?;
    Ok(())
}

pub async fn filesystem_cli(command: FilesystemCommand) -> Result<(), ImlManagerCliError> {
    match command {
        FilesystemCommand::List => {
            let filesystems: ApiList<Filesystem> =
                wrap_fut("Fetching filesystems...", get_all()).await?;

            tracing::debug!("FSs: {:?}", filesystems);

            let table = generate_table(
                &[
                    "Name", "State", "Space", "Inodes", "Clients", "MDTs", "OSTs",
                ],
                filesystems.objects.into_iter().map(|f| {
                    vec![
                        f.label,
                        f.state,
                        usage(f.bytes_free, f.bytes_total, format_bytes),
                        usage(f.files_free, f.files_total, format_number),
                        format_number(f.client_count.unwrap_or(0.0), Some(0)),
                        f.mdts.len().to_string(),
                        f.osts.len().to_string(),
                    ]
                }),
            );

            table.printstd();
        }
        FilesystemCommand::Show { fsname } => {
            let fs: Filesystem =
                wrap_fut("Fetching filesystem...", get_one(vec![("name", &fsname)])).await?;

            tracing::debug!("FS: {:?}", fs);

            let (mgt, osts): (Mgt, Vec<Ost>) = try_join(
                wrap_fut("Fetching MGT...", get(&fs.mgt, Mgt::query())),
                try_join_all(fs.osts.into_iter().map(|o| async move {
                    wrap_fut("Fetching OST...", get(&o, Ost::query())).await
                })),
            )
            .await?;

            let mut table = Table::new();
            table.add_row(Row::from(&["Name".to_string(), fs.label]));
            table.add_row(Row::from(&[
                "Space".to_string(),
                usage(fs.bytes_free, fs.bytes_total, format_bytes),
            ]));
            table.add_row(Row::from(&[
                "Inodes".to_string(),
                usage(fs.files_free, fs.files_total, format_number),
            ]));
            table.add_row(Row::from(&["State".to_string(), fs.state]));
            table.add_row(Row::from(&[
                "Management Server".to_string(),
                mgt.active_host_name,
            ]));

            let mdtnames: Vec<String> = fs.mdts.into_iter().map(|m| m.name).collect();
            table.add_row(Row::from(&["MDTs".to_string(), mdtnames.join("\n")]));

            let ostnames: Vec<String> = osts.into_iter().map(|m| m.name).collect();
            table.add_row(Row::from(&["OSTs".to_string(), ostnames.join("\n")]));

            table.add_row(Row::from(&[
                "Clients".to_string(),
                format!("{:0}", fs.client_count.unwrap_or(0.0)),
            ]));
            table.add_row(Row::from(&["Mount Path".to_string(), fs.mount_path]));
            table.printstd();
        }
        FilesystemCommand::Pool { command } => ostpool_cli(command).await?,
        FilesystemCommand::Detect { hosts } => detect_filesystem(hosts).await?,
    };

    Ok(())
}
<|endoftext|>


<commit_before>//! Implementations of the default callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
//! This is the default mode that Way Cooler is in at initilization
use rustwlc::*;
use rustwlc::input::{pointer, keyboard};
use rustwlc::render::{read_pixels, wlc_pixel_format};
use uuid::Uuid;

use super::{EVENT_BLOCKED, EVENT_PASS_THROUGH, LEFT_CLICK, RIGHT_CLICK};
use ::keys::{self, KeyPress, KeyEvent};
use ::layout::{lock_tree, try_lock_tree, try_lock_action, Action, ContainerType,
                    MovementError, TreeError, FocusError};
use ::layout::commands::set_performing_action;
use ::layout::MIN_SIZE;
use ::lua::{self, LuaQuery};

use ::render::screen_scrape::{read_screen_scrape_lock, scraped_pixels_lock,
                              sync_scrape};

use registry::{self};
use super::Mode;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Default;

#[allow(unused)]
impl Mode for Default {
    fn output_created(&mut self, output: WlcOutput) -> bool {
        if let Ok(mut tree) = try_lock_tree() {
            let result = tree.add_output(output).and_then(|_|{
                tree.switch_to_workspace(&"1")
                    .map(|_| tree.layout_active_of(ContainerType::Output))
            });
            match result {
                // If the output exists, we just couldn't add it to the tree because
                // it's already there. That's OK
                Ok(_) | Err(TreeError::OutputExists(_)) => true,
                _ => false
            }
        } else {
            false
        }
    }

    fn output_destroyed(&mut self, output: WlcOutput) {
         // TODO Redistribute workspaces of that output.
    }

    fn output_focused(&mut self, output: WlcOutput, focused: bool) {
         // TODO Ensure focused on right workspace
    }

    fn output_resolution(&mut self, output: WlcOutput,
                                    old_size_ptr: Size, new_size_ptr: Size) {
        // Update the resolution of the output and its children
        let scale = 1;
        output.set_resolution(new_size_ptr, scale);
        if let Ok(mut tree) = try_lock_tree() {
            tree.layout_active_of(ContainerType::Output)
                .expect("Could not layout active output");
        }
    }

    fn output_render_post(&mut self, output: WlcOutput) {
        let need_to_fetch = read_screen_scrape_lock();
        if *need_to_fetch {
            if WlcOutput::focused() != output {
                return
            }
            if let Ok(mut scraped_pixels) = scraped_pixels_lock() {
                let resolution = output.get_resolution()
                    .expect("Output had no resolution");
                let geo = Geometry {
                    origin: Point { x: 0, y: 0 },
                    size: resolution
                };
                let result = read_pixels(wlc_pixel_format::WLC_RGBA8888, geo).1;
                *scraped_pixels = result;
                sync_scrape();
            }
        }
    }

    fn view_created(&mut self, view: WlcView) -> bool {
        let lock = registry::clients_read();
        let client = lock.client(Uuid::nil()).unwrap();
        let handle = registry::ReadHandle::new(&client);
        let bar = handle.read("programs".into())
            .expect("programs category didn't exist")
            .get("x11_bar".into())
            .and_then(|data| data.as_string().map(str::to_string));
        // TODO Move this hack, probably could live somewhere else
        if let Some(bar_name) = bar {
            if view.get_title().as_str() == bar_name {
                view.set_mask(1);
                view.bring_to_front();
                if let Ok(mut tree) = try_lock_tree() {
                    for output in WlcOutput::list() {
                        tree.add_bar(view, output).unwrap_or_else(|_| {
                            warn!("Could not add bar {:#?} to output {:#?}", view, output);
                        });
                    }
                    return true;
                }
            }
        }
        // TODO Remove this hack
        if view.get_class().as_str() == "Background" {
            trace!("Setting background: {}", view.get_title());
            view.send_to_back();
            view.set_mask(1);
            let output = view.get_output();
            let resolution = output.get_resolution()
                .expect("Couldn't get output resolution");
            let fullscreen = Geometry {
                origin: Point { x: 0, y: 0 },
                size: resolution
            };
            view.set_geometry(ResizeEdge::empty(), fullscreen);
            if let Ok(mut tree) = lock_tree() {
                let output = tree.outputs()[0];
                return tree.add_background(view, output).map(|_| true)
                    .unwrap_or_else(|err| {
                        warn!("Could not add background due to {:?}", err);
                        true
                    })
            } else {
                warn!("Could not lock tree to place background");
            }
            return false
        }
        if let Ok(mut tree) = lock_tree() {
            let result = tree.add_view(view).and_then(|_| {
                view.set_state(VIEW_MAXIMIZED, true);
                match tree.set_active_view(view) {
                    // If blocked by fullscreen, we don't focus on purpose
                    Err(TreeError::Focus(FocusError::BlockedByFullscreen(_, _))) => Ok(()),
                    result => result
                }
            });
            if result.is_err() {
                warn!("Could not add {:?}. Reason: {:?}", view, result);
            }
            result.is_ok()
        } else {
            false
        }
    }

    fn view_destroyed(&mut self, view: WlcView) {
        match try_lock_tree() {
            Ok(mut tree) => {
                tree.remove_view(view).unwrap_or_else(|err| {
                    match err {
                        TreeError::ViewNotFound(_) => {},
                        _ => {
                            warn!("Error in view_destroyed: {:?}", err);
                        }
                    }});
            },
            Err(err) => warn!("Could not delete view {:?}, {:?}", view, err)
        }
    }

    fn view_focused(&mut self, current: WlcView, focused: bool) {
        current.set_state(VIEW_ACTIVATED, focused);
        if let Ok(mut tree) = try_lock_tree() {
            match tree.set_active_view(current) {
                Ok(_) => {},
                Err(err) => {
                    warn!("Could not set {:?} to be active view: {:?}", current, err);
                }
            }
        }
    }

    fn view_props_changed(&mut self, view: WlcView, prop: ViewPropertyType) {
        if prop.contains(PROPERTY_TITLE) {
            if let Ok(mut tree) = try_lock_tree() {
                match tree.update_title(view) {
                    Ok(_) => {},
                    Err(err) => {
                        warn!("Could not update title for view {:?} because {:#?}",
                            view, err);
                    }
                }
            }
        }
    }

    fn view_moved_to_output(&mut self, view: WlcView, o1: WlcOutput, o2: WlcOutput) {
        // TODO Ensure in correct workspace
    }

    fn view_request_state(&mut self, view: WlcView, state: ViewState, toggle: bool) {
        if state == VIEW_FULLSCREEN {
            if let Ok(mut tree) = try_lock_tree() {
                if let Ok(id) = tree.lookup_handle(view.into()) {
                    tree.set_fullscreen(id, toggle)
                        .expect("The ID was related to a non-view, somehow!");
                    match tree.container_in_active_workspace(id) {
                        Ok(true) => {
                            tree.layout_active_of(ContainerType::Workspace)
                                .unwrap_or_else(|err| {
                                    warn!("Could not layout active workspace \
                                           for view {:?}: {:?}",
                                            view, err)
                                });
                        },
                        Ok(false) => {},
                        Err(err) => warn!("Could not set {:?} fullscreen: {:?}",
                                           view, err)
                    }
                } else {
                    warn!("Could not find view {:?} in tree", view);
                }
            }
        }
    }

    fn view_request_move(&mut self, view: WlcView, _dest: Point) {
        if let Ok(mut tree) = try_lock_tree() {
            if let Err(err) = tree.set_active_view(view) {
                warn!("view_request_move error: {:?}", err);
            }
        }
    }

    fn view_request_resize(&mut self, view: WlcView, edge: ResizeEdge, point: Point) {
        if let Ok(mut tree) = try_lock_tree() {
            let in_action = try_lock_action()
                .map(|action| action.is_some())
                .unwrap_or(false);
            if in_action {
                if let Ok(id) = tree.lookup_handle(view.into()) {
                    if let Err(err) = tree.resize_container(id, edge, point) {
                        warn!("resize_container returned error: {:#?}",
                                err);
                    }
                }
            }
        }
    }

    fn on_keyboard_key(&mut self, _view: WlcView, _time: u32, mods: KeyboardModifiers,
                            key: u32, state: KeyState) -> bool {
        let empty_mods: KeyboardModifiers = KeyboardModifiers {
                mods: MOD_NONE,
                leds: KeyboardLed::empty()
        };
        let sym = keyboard::get_keysym_for_key(key, empty_mods);
        let press = KeyPress::new(mods.mods, sym);

        if state == KeyState::Pressed {
            if let Some(key) = keys::get(&press) {
                info!("[key] Found an action for {}, blocking event", press);
                let action = key.event;
                let passthrough = key.passthrough;
                match action {
                    KeyEvent::Command(func) => {
                        func();
                    },
                    KeyEvent::Lua => {
                        match lua::send(LuaQuery::HandleKey(press)) {
                            Ok(_) => {},
                            Err(err) => {
                                // We may want to wait for Lua's reply from
                                // keypresses; for example if the table is tampered
                                // with or Lua is restarted or Lua has an error.
                                // ATM Lua asynchronously logs this but in the future
                                // an error popup/etc is a good idea.
                                warn!("Error sending keypress: {:?}", err);
                            }
                        }
                    }
                }
                return !passthrough
            }
        }

        return EVENT_PASS_THROUGH
    }

    fn view_request_geometry(&mut self, view: WlcView, geometry: Geometry) {
        if geometry.size.w < MIN_SIZE.w && geometry.size.h < MIN_SIZE.h {
            warn!("Ignoring requested geometry {:#?}, which is below min {:#?}",
                  geometry, MIN_SIZE);
            return;
        }
        if let Ok(mut tree) = try_lock_tree() {
            match tree.update_floating_geometry(view, geometry) {
                Ok(()) | Err(TreeError::ViewNotFound(_)) => {},
                err => warn!("Could not find view {:#?} \
                              in order to update geometry w/ {:#?} \
                              because of {:#?}",
                             view, geometry, err)
            }
        }
    }

    fn on_pointer_button(&mut self, view: WlcView, _time: u32,
                            mods: KeyboardModifiers, button: u32,
                                state: ButtonState, point: Point) -> bool {
        if state == ButtonState::Pressed {
            let mouse_mod = keys::mouse_modifier();
            if button == LEFT_CLICK && !view.is_root() {
                if let Ok(mut tree) = try_lock_tree() {
                    tree.set_active_view(view).unwrap_or_else(|_| {
                        // still focus on view, even if not in tree.
                        view.focus();
                    });
                    if mods.mods.contains(mouse_mod) {
                        let action = Action {
                            view: view,
                            grab: point,
                            edges: ResizeEdge::empty()
                        };
                        set_performing_action(Some(action));
                    }
                }
            } else if button == RIGHT_CLICK && !view.is_root() {
                info!("User right clicked w/ mods \"{:?}\" on {:?}", mods, view);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.set_active_view(view).ok();
                }
                if mods.mods.contains(mouse_mod) {
                    let action = Action {
                        view: view,
                        grab: point,
                        edges: ResizeEdge::empty()
                    };
                    set_performing_action(Some(action));
                    let geometry = view.get_geometry()
                        .expect("Could not get geometry of the view");
                    let halfw = geometry.origin.x + geometry.size.w as i32 / 2;
                    let halfh = geometry.origin.y + geometry.size.h as i32 / 2;

                    {
                        let mut action: Action = try_lock_action().ok().and_then(|guard| *guard)
                            .unwrap_or(Action {
                                view: view,
                                grab: point,
                                edges: ResizeEdge::empty()
                            });
                        let flag_x = if point.x < halfw {
                            RESIZE_LEFT
                        } else if point.x > halfw {
                            RESIZE_RIGHT
                        } else {
                            ResizeEdge::empty()
                        };

                        let flag_y = if point.y < halfh {
                            RESIZE_TOP
                        } else if point.y > halfh {
                            RESIZE_BOTTOM
                        } else {
                            ResizeEdge::empty()
                        };

                        action.edges = flag_x | flag_y;
                        set_performing_action(Some(action));
                    }
                    view.set_state(VIEW_RESIZING, true);
                    return EVENT_BLOCKED
                }
            }
        } else {
            if let Ok(lock) = try_lock_action() {
                match *lock {
                    Some(action) => {
                        let view = action.view;
                        if view.get_state().contains(VIEW_RESIZING) {
                            view.set_state(VIEW_RESIZING, false);
                        }
                    },
                    _ => {}
                }
            }
            set_performing_action(None);
        }
        EVENT_PASS_THROUGH
    }

    fn on_pointer_scroll(&mut self, _view: WlcView, _time: u32,
                            _mods_ptr: KeyboardModifiers, _axis: ScrollAxis,
                            _heights: [f64; 2]) -> bool {
        EVENT_PASS_THROUGH
    }

    fn on_pointer_motion(&mut self, view: WlcView, _time: u32, point: Point) -> bool {
        let mut result = EVENT_PASS_THROUGH;
        let mut maybe_action = None;
        {
            if let Ok(action_lock) = try_lock_action() {
                maybe_action = action_lock.clone();
            }
        }
        match maybe_action {
            None => result = EVENT_PASS_THROUGH,
            Some(action) => {
                if action.edges.bits() != 0 {
                    if let Ok(mut tree) = try_lock_tree() {
                        if let Ok(active_id) = tree.lookup_handle(view.into()) {
                            match tree.resize_container(active_id, action.edges, point) {
                                // Return early here to not set the pointer
                                Ok(_) => return EVENT_BLOCKED,
                                Err(err) => warn!("Could not resize: {:#?}", err)
                            }
                        }
                    }
                } else {
                    if let Ok(mut tree) = try_lock_tree() {
                        match tree.try_drag_active(point) {
                            Ok(_) => result = EVENT_BLOCKED,
                            Err(TreeError::PerformingAction(_)) |
                            Err(TreeError::Movement(MovementError::NotFloating(_))) =>
                                result = EVENT_PASS_THROUGH,
                            Err(err) => {
                                warn!("Unexpected drarg error: {:#?}", err);
                                result = EVENT_PASS_THROUGH
                            }
                        }
                    }
                }
            }
        }
        pointer::set_position(point);
        result
    }

    fn view_pre_render(&mut self, view: WlcView) {
        if let Ok(mut tree) = lock_tree() {
            tree.render_borders(view).unwrap_or_else(|err| {
                match err {
                    // TODO Only filter if background or bar
                    TreeError::ViewNotFound(_) => {},
                    err => warn!("Error while rendering borders: {:?}", err)
                }
            })
        }
    }
}
<commit_msg>Do not limit unmanaged views. Fixes #364<commit_after>//! Implementations of the default callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
//! This is the default mode that Way Cooler is in at initilization
use rustwlc::*;
use rustwlc::input::{pointer, keyboard};
use rustwlc::render::{read_pixels, wlc_pixel_format};
use uuid::Uuid;

use super::{EVENT_BLOCKED, EVENT_PASS_THROUGH, LEFT_CLICK, RIGHT_CLICK};
use ::keys::{self, KeyPress, KeyEvent};
use ::layout::{lock_tree, try_lock_tree, try_lock_action, Action, ContainerType,
                    MovementError, TreeError, FocusError};
use ::layout::commands::set_performing_action;
use ::layout::MIN_SIZE;
use ::lua::{self, LuaQuery};

use ::render::screen_scrape::{read_screen_scrape_lock, scraped_pixels_lock,
                              sync_scrape};

use registry::{self};
use super::Mode;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Default;

#[allow(unused)]
impl Mode for Default {
    fn output_created(&mut self, output: WlcOutput) -> bool {
        if let Ok(mut tree) = try_lock_tree() {
            let result = tree.add_output(output).and_then(|_|{
                tree.switch_to_workspace(&"1")
                    .map(|_| tree.layout_active_of(ContainerType::Output))
            });
            match result {
                // If the output exists, we just couldn't add it to the tree because
                // it's already there. That's OK
                Ok(_) | Err(TreeError::OutputExists(_)) => true,
                _ => false
            }
        } else {
            false
        }
    }

    fn output_destroyed(&mut self, output: WlcOutput) {
         // TODO Redistribute workspaces of that output.
    }

    fn output_focused(&mut self, output: WlcOutput, focused: bool) {
         // TODO Ensure focused on right workspace
    }

    fn output_resolution(&mut self, output: WlcOutput,
                                    old_size_ptr: Size, new_size_ptr: Size) {
        // Update the resolution of the output and its children
        let scale = 1;
        output.set_resolution(new_size_ptr, scale);
        if let Ok(mut tree) = try_lock_tree() {
            tree.layout_active_of(ContainerType::Output)
                .expect("Could not layout active output");
        }
    }

    fn output_render_post(&mut self, output: WlcOutput) {
        let need_to_fetch = read_screen_scrape_lock();
        if *need_to_fetch {
            if WlcOutput::focused() != output {
                return
            }
            if let Ok(mut scraped_pixels) = scraped_pixels_lock() {
                let resolution = output.get_resolution()
                    .expect("Output had no resolution");
                let geo = Geometry {
                    origin: Point { x: 0, y: 0 },
                    size: resolution
                };
                let result = read_pixels(wlc_pixel_format::WLC_RGBA8888, geo).1;
                *scraped_pixels = result;
                sync_scrape();
            }
        }
    }

    fn view_created(&mut self, view: WlcView) -> bool {
        let lock = registry::clients_read();
        let client = lock.client(Uuid::nil()).unwrap();
        let handle = registry::ReadHandle::new(&client);
        let bar = handle.read("programs".into())
            .expect("programs category didn't exist")
            .get("x11_bar".into())
            .and_then(|data| data.as_string().map(str::to_string));
        // TODO Move this hack, probably could live somewhere else
        if let Some(bar_name) = bar {
            if view.get_title().as_str() == bar_name {
                view.set_mask(1);
                view.bring_to_front();
                if let Ok(mut tree) = try_lock_tree() {
                    for output in WlcOutput::list() {
                        tree.add_bar(view, output).unwrap_or_else(|_| {
                            warn!("Could not add bar {:#?} to output {:#?}", view, output);
                        });
                    }
                    return true;
                }
            }
        }
        // TODO Remove this hack
        if view.get_class().as_str() == "Background" {
            trace!("Setting background: {}", view.get_title());
            view.send_to_back();
            view.set_mask(1);
            let output = view.get_output();
            let resolution = output.get_resolution()
                .expect("Couldn't get output resolution");
            let fullscreen = Geometry {
                origin: Point { x: 0, y: 0 },
                size: resolution
            };
            view.set_geometry(ResizeEdge::empty(), fullscreen);
            if let Ok(mut tree) = lock_tree() {
                let output = tree.outputs()[0];
                return tree.add_background(view, output).map(|_| true)
                    .unwrap_or_else(|err| {
                        warn!("Could not add background due to {:?}", err);
                        true
                    })
            } else {
                warn!("Could not lock tree to place background");
            }
            return false
        }
        if let Ok(mut tree) = lock_tree() {
            let result = tree.add_view(view).and_then(|_| {
                view.set_state(VIEW_MAXIMIZED, true);
                match tree.set_active_view(view) {
                    // If blocked by fullscreen, we don't focus on purpose
                    Err(TreeError::Focus(FocusError::BlockedByFullscreen(_, _))) => Ok(()),
                    result => result
                }
            });
            if result.is_err() {
                warn!("Could not add {:?}. Reason: {:?}", view, result);
            }
            result.is_ok()
        } else {
            false
        }
    }

    fn view_destroyed(&mut self, view: WlcView) {
        match try_lock_tree() {
            Ok(mut tree) => {
                tree.remove_view(view).unwrap_or_else(|err| {
                    match err {
                        TreeError::ViewNotFound(_) => {},
                        _ => {
                            warn!("Error in view_destroyed: {:?}", err);
                        }
                    }});
            },
            Err(err) => warn!("Could not delete view {:?}, {:?}", view, err)
        }
    }

    fn view_focused(&mut self, current: WlcView, focused: bool) {
        current.set_state(VIEW_ACTIVATED, focused);
        if let Ok(mut tree) = try_lock_tree() {
            match tree.set_active_view(current) {
                Ok(_) => {},
                Err(err) => {
                    warn!("Could not set {:?} to be active view: {:?}", current, err);
                }
            }
        }
    }

    fn view_props_changed(&mut self, view: WlcView, prop: ViewPropertyType) {
        if prop.contains(PROPERTY_TITLE) {
            if let Ok(mut tree) = try_lock_tree() {
                match tree.update_title(view) {
                    Ok(_) => {},
                    Err(err) => {
                        warn!("Could not update title for view {:?} because {:#?}",
                            view, err);
                    }
                }
            }
        }
    }

    fn view_moved_to_output(&mut self, view: WlcView, o1: WlcOutput, o2: WlcOutput) {
        // TODO Ensure in correct workspace
    }

    fn view_request_state(&mut self, view: WlcView, state: ViewState, toggle: bool) {
        if state == VIEW_FULLSCREEN {
            if let Ok(mut tree) = try_lock_tree() {
                if let Ok(id) = tree.lookup_handle(view.into()) {
                    tree.set_fullscreen(id, toggle)
                        .expect("The ID was related to a non-view, somehow!");
                    match tree.container_in_active_workspace(id) {
                        Ok(true) => {
                            tree.layout_active_of(ContainerType::Workspace)
                                .unwrap_or_else(|err| {
                                    warn!("Could not layout active workspace \
                                           for view {:?}: {:?}",
                                            view, err)
                                });
                        },
                        Ok(false) => {},
                        Err(err) => warn!("Could not set {:?} fullscreen: {:?}",
                                           view, err)
                    }
                } else {
                    warn!("Could not find view {:?} in tree", view);
                }
            }
        }
    }

    fn view_request_move(&mut self, view: WlcView, _dest: Point) {
        if let Ok(mut tree) = try_lock_tree() {
            if let Err(err) = tree.set_active_view(view) {
                warn!("view_request_move error: {:?}", err);
            }
        }
    }

    fn view_request_resize(&mut self, view: WlcView, edge: ResizeEdge, point: Point) {
        if let Ok(mut tree) = try_lock_tree() {
            let in_action = try_lock_action()
                .map(|action| action.is_some())
                .unwrap_or(false);
            if in_action {
                if let Ok(id) = tree.lookup_handle(view.into()) {
                    if let Err(err) = tree.resize_container(id, edge, point) {
                        warn!("resize_container returned error: {:#?}",
                                err);
                    }
                }
            }
        }
    }

    fn on_keyboard_key(&mut self, _view: WlcView, _time: u32, mods: KeyboardModifiers,
                            key: u32, state: KeyState) -> bool {
        let empty_mods: KeyboardModifiers = KeyboardModifiers {
                mods: MOD_NONE,
                leds: KeyboardLed::empty()
        };
        let sym = keyboard::get_keysym_for_key(key, empty_mods);
        let press = KeyPress::new(mods.mods, sym);

        if state == KeyState::Pressed {
            if let Some(key) = keys::get(&press) {
                info!("[key] Found an action for {}, blocking event", press);
                let action = key.event;
                let passthrough = key.passthrough;
                match action {
                    KeyEvent::Command(func) => {
                        func();
                    },
                    KeyEvent::Lua => {
                        match lua::send(LuaQuery::HandleKey(press)) {
                            Ok(_) => {},
                            Err(err) => {
                                // We may want to wait for Lua's reply from
                                // keypresses; for example if the table is tampered
                                // with or Lua is restarted or Lua has an error.
                                // ATM Lua asynchronously logs this but in the future
                                // an error popup/etc is a good idea.
                                warn!("Error sending keypress: {:?}", err);
                            }
                        }
                    }
                }
                return !passthrough
            }
        }

        return EVENT_PASS_THROUGH
    }

    fn view_request_geometry(&mut self, view: WlcView, geometry: Geometry) {
        let unmanaged = view.get_type().intersects(VIEW_BIT_UNMANAGED);
        if geometry.size.w < MIN_SIZE.w && geometry.size.h < MIN_SIZE.h && !unmanaged {
            warn!("Ignoring requested geometry {:#?}, which is below min {:#?}",
                  geometry, MIN_SIZE);
            return;
        }
        if let Ok(mut tree) = try_lock_tree() {
            match tree.update_floating_geometry(view, geometry) {
                Ok(()) | Err(TreeError::ViewNotFound(_)) => {},
                err => warn!("Could not find view {:#?} \
                              in order to update geometry w/ {:#?} \
                              because of {:#?}",
                             view, geometry, err)
            }
        }
    }

    fn on_pointer_button(&mut self, view: WlcView, _time: u32,
                            mods: KeyboardModifiers, button: u32,
                                state: ButtonState, point: Point) -> bool {
        if state == ButtonState::Pressed {
            let mouse_mod = keys::mouse_modifier();
            if button == LEFT_CLICK && !view.is_root() {
                if let Ok(mut tree) = try_lock_tree() {
                    tree.set_active_view(view).unwrap_or_else(|_| {
                        // still focus on view, even if not in tree.
                        view.focus();
                    });
                    if mods.mods.contains(mouse_mod) {
                        let action = Action {
                            view: view,
                            grab: point,
                            edges: ResizeEdge::empty()
                        };
                        set_performing_action(Some(action));
                    }
                }
            } else if button == RIGHT_CLICK && !view.is_root() {
                info!("User right clicked w/ mods \"{:?}\" on {:?}", mods, view);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.set_active_view(view).ok();
                }
                if mods.mods.contains(mouse_mod) {
                    let action = Action {
                        view: view,
                        grab: point,
                        edges: ResizeEdge::empty()
                    };
                    set_performing_action(Some(action));
                    let geometry = view.get_geometry()
                        .expect("Could not get geometry of the view");
                    let halfw = geometry.origin.x + geometry.size.w as i32 / 2;
                    let halfh = geometry.origin.y + geometry.size.h as i32 / 2;

                    {
                        let mut action: Action = try_lock_action().ok().and_then(|guard| *guard)
                            .unwrap_or(Action {
                                view: view,
                                grab: point,
                                edges: ResizeEdge::empty()
                            });
                        let flag_x = if point.x < halfw {
                            RESIZE_LEFT
                        } else if point.x > halfw {
                            RESIZE_RIGHT
                        } else {
                            ResizeEdge::empty()
                        };

                        let flag_y = if point.y < halfh {
                            RESIZE_TOP
                        } else if point.y > halfh {
                            RESIZE_BOTTOM
                        } else {
                            ResizeEdge::empty()
                        };

                        action.edges = flag_x | flag_y;
                        set_performing_action(Some(action));
                    }
                    view.set_state(VIEW_RESIZING, true);
                    return EVENT_BLOCKED
                }
            }
        } else {
            if let Ok(lock) = try_lock_action() {
                match *lock {
                    Some(action) => {
                        let view = action.view;
                        if view.get_state().contains(VIEW_RESIZING) {
                            view.set_state(VIEW_RESIZING, false);
                        }
                    },
                    _ => {}
                }
            }
            set_performing_action(None);
        }
        EVENT_PASS_THROUGH
    }

    fn on_pointer_scroll(&mut self, _view: WlcView, _time: u32,
                            _mods_ptr: KeyboardModifiers, _axis: ScrollAxis,
                            _heights: [f64; 2]) -> bool {
        EVENT_PASS_THROUGH
    }

    fn on_pointer_motion(&mut self, view: WlcView, _time: u32, point: Point) -> bool {
        let mut result = EVENT_PASS_THROUGH;
        let mut maybe_action = None;
        {
            if let Ok(action_lock) = try_lock_action() {
                maybe_action = action_lock.clone();
            }
        }
        match maybe_action {
            None => result = EVENT_PASS_THROUGH,
            Some(action) => {
                if action.edges.bits() != 0 {
                    if let Ok(mut tree) = try_lock_tree() {
                        if let Ok(active_id) = tree.lookup_handle(view.into()) {
                            match tree.resize_container(active_id, action.edges, point) {
                                // Return early here to not set the pointer
                                Ok(_) => return EVENT_BLOCKED,
                                Err(err) => warn!("Could not resize: {:#?}", err)
                            }
                        }
                    }
                } else {
                    if let Ok(mut tree) = try_lock_tree() {
                        match tree.try_drag_active(point) {
                            Ok(_) => result = EVENT_BLOCKED,
                            Err(TreeError::PerformingAction(_)) |
                            Err(TreeError::Movement(MovementError::NotFloating(_))) =>
                                result = EVENT_PASS_THROUGH,
                            Err(err) => {
                                warn!("Unexpected drarg error: {:#?}", err);
                                result = EVENT_PASS_THROUGH
                            }
                        }
                    }
                }
            }
        }
        pointer::set_position(point);
        result
    }

    fn view_pre_render(&mut self, view: WlcView) {
        if let Ok(mut tree) = lock_tree() {
            tree.render_borders(view).unwrap_or_else(|err| {
                match err {
                    // TODO Only filter if background or bar
                    TreeError::ViewNotFound(_) => {},
                    err => warn!("Error while rendering borders: {:?}", err)
                }
            })
        }
    }
}
<|endoftext|>


<commit_before>use super::{Index, Range};
use small;
use std::{
    cmp::Ordering,
    u8,
};

fn numeric_range<'a>(
    start: isize,
    end: isize,
    step: isize,
    inclusive: bool,
    nb_digits: usize,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    let end = if start < end && inclusive {
        end + 1
    } else if start > end && inclusive {
        end - 1
    } else {
        end
    };

    if step == 0 || (start < end && step < 0) || (start > end && step > 0) {
        None
    } else {
        let (x, y, ordering) = if start < end {
            (start, end, Ordering::Greater)
        } else {
            (end, start, Ordering::Less)
        };

        let iter = (x..y).scan(start, move |index, _| {
            if end.cmp(index) == ordering {
                let index_holder = *index;
                *index += step; // This step adds
                Some(format!("{:0width$}", index_holder, width = nb_digits).into())
            } else {
                None
            }
        });

        Some(Box::new(iter))
    }
}

#[inline]
fn byte_is_valid_range(b: u8) -> bool { (b >= b'a' && b <= b'z') || (b >= b'A' && b <= b'Z') }

fn char_range<'a>(
    start: u8,
    end: u8,
    step: isize,
    inclusive: bool,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    let char_step = step.checked_abs()?;
    let step = char_step as u8;

    if !byte_is_valid_range(start) || !byte_is_valid_range(end) || char_step > u8::MAX as isize || step == 0 {
        return None;
    }

    let end = if start < end && inclusive {
        end + 1
    } else if start > end && inclusive {
        end - 1
    } else {
        end
    };

    let (x, y, ordering) = if start < end {
        (start, end, Ordering::Greater)
    } else {
        (end, start, Ordering::Less)
    };

    let iter = (x..y).scan(start, move |index, _| {
        if end.cmp(index) == ordering {
            let index_holder = *index;
            *index = match ordering {
                Ordering::Greater => index.wrapping_add(step),
                Ordering::Less => index.wrapping_sub(step),
                _ => unreachable!(),
            };
            Some((index_holder as char).to_string().into())
        } else {
            None
        }
    });

    Some(Box::new(iter))
}

fn count_minimum_digits(a: &str) -> usize {
    for c in a.chars() {
        match c {
            '-' => (),
            '0' => return a.len(),
            '1'...'9' => break,
            _ => panic!("count_minimum_digits should only be called for a valid number."),
        }
    }
    0
}

fn strings_to_isizes(a: &str, b: &str) -> Option<(isize, isize, usize)> {
    let first: isize = a.parse().ok()?;
    let second: isize = b.parse().ok()?;
    let nb_digits = usize::max(count_minimum_digits(a), count_minimum_digits(b));
    Some((first, second, nb_digits))
}

// TODO: Make this an iterator structure.
// In a range we allow the following syntax:
//      Exclusive nonstepped: {start..end}
//      Inclusive nonstepped: {start...end}
//      Exclusive stepped: {start..step..end}
//      Inclusive stepped: {start..step...end}
pub fn parse_range(input: &str) -> Option<Box<Iterator<Item = small::String>>> {
    let mut read = 0;
    let mut bytes_iterator = input.bytes();
    while let Some(byte) = bytes_iterator.next() {
        match byte {
            // can only find these as the first byte, otherwise the syntax is bad
            b'a'...b'z' | b'-' | b'A'...b'Z' if read == 0 => read += 1,
            b'0'...b'9' => read += 1,
            b'.' => {
                let first = &input[..read];
                read += 1;
                // The next byte has to be a dot to be valid range
                // syntax
                match bytes_iterator.next() {
                    Some(b'.') => read += 1,
                    _ => return None,
                }

                macro_rules! finish_char {
                    ($inclusive:expr, $end_str:expr, $step:expr) => {
                        if first.len() == 1 && $end_str.len() == 1 {
                            let start = first.as_bytes()[0];
                            let end = $end_str.as_bytes()[0];
                            return char_range(start, end, $step, $inclusive);
                        } else {
                            return None;
                        }
                    };
                }

                macro_rules! finish {
                    ($inclusive:expr, $read:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end, nb_digits)) = strings_to_isizes(first, end_str) {
                            return numeric_range(
                                start,
                                end,
                                if start < end { 1 } else { -1 },
                                $inclusive,
                                nb_digits,
                            );
                        } else {
                            finish_char!($inclusive, end_str, 1);
                        }
                    };
                    ($inclusive:expr, $read:expr, $step:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end, nb_digits)) = strings_to_isizes(first, end_str) {
                            return numeric_range(start, end, $step, $inclusive, nb_digits);
                        } else {
                            finish_char!($inclusive, end_str, $step);
                        }
                    };
                }

                // if the next byte is a dot we're certain it is an inclusive
                // unstepped range otherwise it has to be [-0-9a-zA-Z]
                if let Some(b) = bytes_iterator.next() {
                    read += 1;
                    match b {
                        b'.' | b'=' => {
                            // this can only be an inclusive range
                            finish!(true, read);
                        }
                        b'0'...b'9' | b'-' | b'a'...b'z' | b'A'...b'Z' => {
                            // further processing needed to find out if we're reading a step or
                            // the end of an exclusive range. Step until we find another dot or
                            // the iterator ends
                            let start = read - 1;
                            while let Some(b) = bytes_iterator.next() {
                                read += 1;
                                match b {
                                    b'.' => {
                                        // stepped range input[start..read - 1] contains the step
                                        // size
                                        let step = (&input[start..read - 1]).parse::<isize>().ok()?;
                                        // count the dots to determine inclusive/exclusive
                                        let mut dots = 1;
                                        while let Some(b) = bytes_iterator.next() {
                                            read += 1;
                                            match b {
                                                b'.' => dots += 1,
                                                _ => break,
                                            }
                                        }
                                        finish!(dots == 3, read - 1, step);
                                    }
                                    // numeric values are OK but no letters anymore
                                    b'0'...b'9' => {}
                                    // unexpected
                                    _ => return None,
                                }
                            }
                            // exhausted the iterator without finding anything new means
                            // exclusive unstepped range
                            finish!(false, start);
                        }
                        // not a valid byte for ranges
                        _ => return None,
                    }
                }
            }
            _ => break,
        }
    }
    None
}

pub fn parse_index_range(input: &str) -> Option<Range> {
    let mut bytes_iterator = input.bytes().enumerate();
    while let Some((id, byte)) = bytes_iterator.next() {
        match byte {
            b'0'...b'9' | b'-' => continue,
            b'.' => {
                let first = &input[..id];

                let mut dots = 1;
                let mut last_byte = 0;

                for (_, byte) in bytes_iterator {
                    last_byte = byte;
                    if byte == b'.' {
                        dots += 1
                    } else {
                        break;
                    }
                }

                if dots < 2 || dots > 3 {
                    break;
                }

                let inclusive = dots == 3 || (dots == 2 && last_byte == b'=');

                let end = &input[id + dots..];

                return if first.is_empty() && !end.is_empty() {
                    end.parse::<isize>().map(|end| Range::to(Index::new(end))).ok()
                } else if end.is_empty() {
                    first.parse::<isize>().map(|start| Range::from(Index::new(start))).ok()
                } else {
                    first
                        .parse::<isize>()
                        .and_then(|start| end.parse::<isize>().map(|end| (start, end)))
                        .map(|(start, end)| {
                            if inclusive {
                                Range::inclusive(Index::new(start), Index::new(end))
                            } else {
                                Range::exclusive(Index::new(start), Index::new(end))
                            }
                        })
                        .ok()
                }
            }
            _ => break,
        }
    }

    None
}
<commit_msg>Revert "Refactor stepped_range* functions into *_range functions + cleanup"<commit_after>use super::{Index, Range};
use small;
use std::cmp::Ordering;

fn stepped_range_numeric<'a>(
    start: isize,
    end: isize,
    step: isize,
    nb_digits: usize,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    if step == 0 || (start < end && step < 0) || (start > end && step > 0) {
        None
    } else {
        let (x, y, ordering) = if start < end {
            (start, end, Ordering::Greater)
        } else {
            (end, start, Ordering::Less)
        };

        let iter = (x..y).scan(start, move |index, _| {
            if end.cmp(index) == ordering {
                let index_holder = *index;
                *index += step; // This step adds
                Some(format!("{:0width$}", index_holder, width = nb_digits).into())
            } else {
                None
            }
        });

        Some(Box::new(iter))
    }
}

fn stepped_range_chars<'a>(
    start: u8,
    end: u8,
    step: u8,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    if step == 0 {
        None
    } else {
        let (x, y, ordering) = if start < end {
            (start, end, Ordering::Greater)
        } else {
            (end, start, Ordering::Less)
        };

        let iter = (x..y).scan(start, move |index, _| {
            if end.cmp(index) == ordering {
                let index_holder = *index;
                *index = match ordering {
                    Ordering::Greater => index.wrapping_add(step),
                    Ordering::Less => index.wrapping_sub(step),
                    _ => unreachable!(),
                };
                Some((index_holder as char).to_string().into())
            } else {
                None
            }
        });

        Some(Box::new(iter))
    }
}

fn numeric_range<'a>(
    start: isize,
    end: isize,
    step: isize,
    inclusive: bool,
    nb_digits: usize,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    let end = if start < end && inclusive {
        end + 1
    } else if start > end && inclusive {
        end - 1
    } else {
        end
    };
    stepped_range_numeric(start, end, step, nb_digits)
}

#[inline]
fn byte_is_valid_range(b: u8) -> bool { (b >= b'a' && b <= b'z') || (b >= b'A' && b <= b'Z') }

use std::u8;
fn char_range<'a>(
    start: u8,
    end: u8,
    step: isize,
    inclusive: bool,
) -> Option<Box<Iterator<Item = small::String> + 'a>> {
    if !byte_is_valid_range(start) || !byte_is_valid_range(end) {
        return None;
    }

    let char_step = {
        let v = step.checked_abs()?;
        if v > u8::MAX as isize {
            return None;
        }
        v as u8
    };

    let end = if start < end && inclusive {
        end + 1
    } else if start > end && inclusive {
        end - 1
    } else {
        end
    };
    stepped_range_chars(start, end, char_step)
}

fn count_minimum_digits(a: &str) -> usize {
    for c in a.chars() {
        match c {
            '-' => (),
            '0' => return a.len(),
            '1'...'9' => break,
            _ => panic!("count_minimum_digits should only be called for a valid number."),
        }
    }
    0
}

fn strings_to_isizes(a: &str, b: &str) -> Option<(isize, isize, usize)> {
    let first: isize = a.parse().ok()?;
    let second: isize = b.parse().ok()?;
    let nb_digits = usize::max(count_minimum_digits(a), count_minimum_digits(b));
    Some((first, second, nb_digits))
}

// TODO: Make this an iterator structure.
// In a range we allow the following syntax:
//      Exclusive nonstepped: {start..end}
//      Inclusive nonstepped: {start...end}
//      Exclusive stepped: {start..step..end}
//      Inclusive stepped: {start..step...end}
pub fn parse_range(input: &str) -> Option<Box<Iterator<Item = small::String>>> {
    let mut read = 0;
    let mut bytes_iterator = input.bytes();
    while let Some(byte) = bytes_iterator.next() {
        match byte {
            // can only find these as the first byte, otherwise the syntax is bad
            b'a'...b'z' | b'-' | b'A'...b'Z' if read == 0 => read += 1,
            b'0'...b'9' => read += 1,
            b'.' => {
                let first = &input[..read];
                read += 1;
                // The next byte has to be a dot to be valid range
                // syntax
                match bytes_iterator.next() {
                    Some(b'.') => read += 1,
                    _ => return None,
                }

                macro_rules! finish_char {
                    ($inclusive:expr, $end_str:expr, $step:expr) => {
                        if first.len() == 1 && $end_str.len() == 1 {
                            let start = first.as_bytes()[0];
                            let end = $end_str.as_bytes()[0];
                            return char_range(start, end, $step, $inclusive);
                        } else {
                            return None;
                        }
                    };
                }

                macro_rules! finish {
                    ($inclusive:expr, $read:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end, nb_digits)) = strings_to_isizes(first, end_str) {
                            return numeric_range(
                                start,
                                end,
                                if start < end { 1 } else { -1 },
                                $inclusive,
                                nb_digits,
                            );
                        } else {
                            finish_char!($inclusive, end_str, 1);
                        }
                    };
                    ($inclusive:expr, $read:expr, $step:expr) => {
                        let end_str = &input[$read..];
                        if let Some((start, end, nb_digits)) = strings_to_isizes(first, end_str) {
                            return numeric_range(start, end, $step, $inclusive, nb_digits);
                        } else {
                            finish_char!($inclusive, end_str, $step);
                        }
                    };
                }

                // if the next byte is a dot we're certain it is an inclusive
                // unstepped range otherwise it has to be [-0-9a-zA-Z]
                if let Some(b) = bytes_iterator.next() {
                    read += 1;
                    match b {
                        b'.' | b'=' => {
                            // this can only be an inclusive range
                            finish!(true, read);
                        }
                        b'0'...b'9' | b'-' | b'a'...b'z' | b'A'...b'Z' => {
                            // further processing needed to find out if we're reading a step or
                            // the end of an exclusive range. Step until we find another dot or
                            // the iterator ends
                            let start = read - 1;
                            while let Some(b) = bytes_iterator.next() {
                                read += 1;
                                match b {
                                    b'.' => {
                                        // stepped range input[start..read - 1] contains the step
                                        // size
                                        let step = match (&input[start..read - 1]).parse::<isize>()
                                        {
                                            Ok(v) => v,
                                            Err(_) => return None,
                                        };
                                        // count the dots to determine inclusive/exclusive
                                        let mut dots = 1;
                                        while let Some(b) = bytes_iterator.next() {
                                            read += 1;
                                            match b {
                                                b'.' => dots += 1,
                                                _ => break,
                                            }
                                        }
                                        finish!(dots == 3, read - 1, step);
                                    }
                                    // numeric values are OK but no letters anymore
                                    b'0'...b'9' => {}
                                    // unexpected
                                    _ => return None,
                                }
                            }
                            // exhausted the iterator without finding anything new means
                            // exclusive unstepped range
                            finish!(false, start);
                        }
                        // not a valid byte for ranges
                        _ => return None,
                    }
                }
            }
            _ => break,
        }
    }
    None
}

pub fn parse_index_range(input: &str) -> Option<Range> {
    let mut bytes_iterator = input.bytes().enumerate();
    while let Some((id, byte)) = bytes_iterator.next() {
        match byte {
            b'0'...b'9' | b'-' => continue,
            b'.' => {
                let first = &input[..id];

                let mut dots = 1;
                let mut last_byte = 0;

                for (_, byte) in bytes_iterator {
                    last_byte = byte;
                    if byte == b'.' {
                        dots += 1
                    } else {
                        break;
                    }
                }

                if dots < 2 || dots > 3 {
                    break;
                }

                let inclusive = dots == 3 || (dots == 2 && last_byte == b'=');

                let end = &input[id + if inclusive { 3 } else { 2 }..];

                if first.is_empty() {
                    return if end.is_empty() {
                        None
                    } else {
                        end.parse::<isize>().map(|end| Range::to(Index::new(end))).ok()
                    };
                } else if end.is_empty() {
                    return first.parse::<isize>().map(|start| Range::from(Index::new(start))).ok();
                }

                return first
                    .parse::<isize>()
                    .and_then(|start| end.parse::<isize>().map(|end| (start, end)))
                    .map(|(start, end)| {
                        if inclusive {
                            Range::inclusive(Index::new(start), Index::new(end))
                        } else {
                            Range::exclusive(Index::new(start), Index::new(end))
                        }
                    })
                    .ok();
            }
            _ => break,
        }
    }

    None
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn list(rule: Rule) -> Rule {
    match rule {
        Rule { name, ty, expr } => Rule {
            name,
            ty,
            expr: expr.map_bottom_up(|expr| {
                // TODO: Use box syntax when it gets stabilized.
                match expr {
                    Expr::Seq(l, r) => match *l {
                        Expr::Rep(l) => {
                            let l = *l;
                            match l {
                                Expr::Seq(l1, l2) => {
                                    // Converts `(rule ~ rest)* ~ rule` to `rule ~ (rest ~ rule)*`,
                                    // avoiding matching the last `rule` twice.
                                    if l1 == r {
                                        Expr::Seq(
                                            l1,
                                            Box::new(Expr::Rep(Box::new(Expr::Seq(l2, r)))),
                                        )
                                    } else {
                                        Expr::Seq(
                                            Box::new(Expr::Rep(Box::new(Expr::Seq(l1, l2)))),
                                            r,
                                        )
                                    }
                                }
                                expr => Expr::Seq(Box::new(Expr::Rep(Box::new(expr))), r),
                            }
                        }
                        expr => Expr::Seq(Box::new(expr), r),
                    },
                    expr => expr,
                }
            }),
        },
    }
}
<commit_msg>Fix clippy lints in lister.rs<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn list(rule: Rule) -> Rule {
    let Rule { name, ty, expr } = rule;
    Rule {
        name,
        ty,
        expr: expr.map_bottom_up(|expr| {
            // TODO: Use box syntax when it gets stabilized.
            match expr {
                Expr::Seq(l, r) => match *l {
                    Expr::Rep(l) => {
                        let l = *l;
                        match l {
                            Expr::Seq(l1, l2) => {
                                // Converts `(rule ~ rest)* ~ rule` to `rule ~ (rest ~ rule)*`,
                                // avoiding matching the last `rule` twice.
                                if l1 == r {
                                    Expr::Seq(l1, Box::new(Expr::Rep(Box::new(Expr::Seq(l2, r)))))
                                } else {
                                    Expr::Seq(Box::new(Expr::Rep(Box::new(Expr::Seq(l1, l2)))), r)
                                }
                            }
                            expr => Expr::Seq(Box::new(Expr::Rep(Box::new(expr))), r),
                        }
                    }
                    expr => Expr::Seq(Box::new(expr), r),
                },
                expr => expr,
            }
        }),
    }
}
<|endoftext|>