<commit_before>// # References
//
// "The Content-Disposition Header Field" https://www.ietf.org/rfc/rfc2183.txt
// "The Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)" https://www.ietf.org/rfc/rfc6266.txt
// "Returning Values from Forms: multipart/form-data" https://www.ietf.org/rfc/rfc2388.txt
// Browser conformance tests at: http://greenbytes.de/tech/tc2231/
// IANA assignment: http://www.iana.org/assignments/cont-disp/cont-disp.xhtml

use language_tags::LanguageTag;
use header;
use header::{Header, IntoHeaderValue, Writer};
use header::shared::Charset;

use std::fmt::{self, Write};

/// The implied disposition of the content of the HTTP body.
#[derive(Clone, Debug, PartialEq)]
pub enum DispositionType {
    /// Inline implies default processing
    Inline,
    /// Attachment implies that the recipient should prompt the user to save the response locally,
    /// rather than process it normally (as per its media type).
    Attachment,
    /// Extension type.  Should be handled by recipients the same way as Attachment
    Ext(String)
}

/// A parameter to the disposition type.
#[derive(Clone, Debug, PartialEq)]
pub enum DispositionParam {
    /// A Filename consisting of a Charset, an optional LanguageTag, and finally a sequence of
    /// bytes representing the filename
    Filename(Charset, Option<LanguageTag>, Vec<u8>),
    /// Extension type consisting of token and value.  Recipients should ignore unrecognized
    /// parameters.
    Ext(String, String)
}

/// A `Content-Disposition` header, (re)defined in [RFC6266](https://tools.ietf.org/html/rfc6266).
///
/// The Content-Disposition response header field is used to convey
/// additional information about how to process the response payload, and
/// also can be used to attach additional metadata, such as the filename
/// to use when saving the response payload locally.
///
/// # ABNF

/// ```text
/// content-disposition = "Content-Disposition" ":"
///                       disposition-type *( ";" disposition-parm )
///
/// disposition-type    = "inline" | "attachment" | disp-ext-type
///                       ; case-insensitive
///
/// disp-ext-type       = token
///
/// disposition-parm    = filename-parm | disp-ext-parm
///
/// filename-parm       = "filename" "=" value
///                     | "filename*" "=" ext-value
///
/// disp-ext-parm       = token "=" value
///                     | ext-token "=" ext-value
///
/// ext-token           = <the characters in token, followed by "*">
/// ```
///
/// # Example
///
/// ```
/// use actix_web::http::header::{ContentDisposition, DispositionType, DispositionParam, Charset};
///
/// let cd1 = ContentDisposition {
///     disposition: DispositionType::Attachment,
///     parameters: vec![DispositionParam::Filename(
///       Charset::Iso_8859_1, // The character set for the bytes of the filename
///       None, // The optional language tag (see `language-tag` crate)
///       b"\xa9 Copyright 1989.txt".to_vec() // the actual bytes of the filename
///     )]
/// };
///
/// let cd2 = ContentDisposition {
///     disposition: DispositionType::Inline,
///     parameters: vec![DispositionParam::Filename(
///       Charset::Ext("UTF-8".to_owned()),
///       None,
///       "\u{2764}".as_bytes().to_vec()
///     )]
/// };
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct ContentDisposition {
    /// The disposition
    pub disposition: DispositionType,
    /// Disposition parameters
    pub parameters: Vec<DispositionParam>,
}
impl ContentDisposition {
    /// Parse a raw Content-Disposition header value
    pub fn from_raw(hv: &header::HeaderValue) -> Result<Self, ::error::ParseError> {
        header::from_one_raw_str(Some(hv)).and_then(|s: String| {
            let mut sections = s.split(';');
            let disposition = match sections.next() {
                Some(s) => s.trim(),
                None => return Err(::error::ParseError::Header),
            };

            let mut cd = ContentDisposition {
                disposition: if disposition.eq_ignore_ascii_case("inline") {
                    DispositionType::Inline
                } else if disposition.eq_ignore_ascii_case("attachment") {
                    DispositionType::Attachment
                } else {
                    DispositionType::Ext(disposition.to_owned())
                },
                parameters: Vec::new(),
            };

            for section in sections {
                let mut parts = section.splitn(2, '=');

                let key = if let Some(key) = parts.next() {
                    key.trim()
                } else {
                    return Err(::error::ParseError::Header);
                };

                let val = if let Some(val) = parts.next() {
                    val.trim()
                } else {
                    return Err(::error::ParseError::Header);
                };

                cd.parameters.push(
                    if key.eq_ignore_ascii_case("filename") {
                        DispositionParam::Filename(
                            Charset::Ext("UTF-8".to_owned()), None,
                            val.trim_matches('"').as_bytes().to_owned())
                    } else if key.eq_ignore_ascii_case("filename*") {
                        let extended_value = try!(header::parse_extended_value(val));
                        DispositionParam::Filename(extended_value.charset, extended_value.language_tag, extended_value.value)
                    } else {
                        DispositionParam::Ext(key.to_owned(), val.trim_matches('"').to_owned())
                    }
                );
            }

            Ok(cd)
        })
    }
}

impl IntoHeaderValue for ContentDisposition {
    type Error = header::InvalidHeaderValueBytes;

    fn try_into(self) -> Result<header::HeaderValue, Self::Error> {
        let mut writer = Writer::new();
        let _ = write!(&mut writer, "{}", self);
        header::HeaderValue::from_shared(writer.take())
    }
}

impl Header for ContentDisposition {
    fn name() -> header::HeaderName {
        header::CONTENT_DISPOSITION
    }

    fn parse<T: ::HttpMessage>(msg: &T) -> Result<Self, ::error::ParseError> {
        if let Some(h) = msg.headers().get(Self::name()) {
            Self::from_raw(&h)
        } else {
            Err(::error::ParseError::Header)
        }
    }
}

impl fmt::Display for ContentDisposition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.disposition {
            DispositionType::Inline => try!(write!(f, "inline")),
            DispositionType::Attachment => try!(write!(f, "attachment")),
            DispositionType::Ext(ref s) => try!(write!(f, "{}", s)),
        }
        for param in &self.parameters {
            match *param {
                DispositionParam::Filename(ref charset, ref opt_lang, ref bytes) => {
                    let mut use_simple_format: bool = false;
                    if opt_lang.is_none() {
                        if let Charset::Ext(ref ext) = *charset {
                            if ext.eq_ignore_ascii_case("utf-8") {
                                use_simple_format = true;
                            }
                        }
                    }
                    if use_simple_format {
                        try!(write!(f, "; filename=\"{}\"",
                                    match String::from_utf8(bytes.clone()) {
                                        Ok(s) => s,
                                        Err(_) => return Err(fmt::Error),
                                    }));
                    } else {
                        try!(write!(f, "; filename*={}'", charset));
                        if let Some(ref lang) = *opt_lang {
                            try!(write!(f, "{}", lang));
                        };
                        try!(write!(f, "'"));
                        try!(header::http_percent_encode(f, bytes))
                    }
                },
                DispositionParam::Ext(ref k, ref v) => try!(write!(f, "; {}=\"{}\"", k, v)),
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::{ContentDisposition,DispositionType,DispositionParam};
    use header::HeaderValue;
    use header::shared::Charset;
    #[test]
    fn test_from_raw() {
        assert!(ContentDisposition::from_raw(&HeaderValue::from_static("")).is_err());

        let a = HeaderValue::from_static("form-data; dummy=3; name=upload; filename=\"sample.png\"");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![
                DispositionParam::Ext("dummy".to_owned(), "3".to_owned()),
                DispositionParam::Ext("name".to_owned(), "upload".to_owned()),
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    "sample.png".bytes().collect()) ]
        };
        assert_eq!(a, b);

        let a = HeaderValue::from_static("attachment; filename=\"image.jpg\"");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Attachment,
            parameters: vec![
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    "image.jpg".bytes().collect()) ]
        };
        assert_eq!(a, b);

        let a = HeaderValue::from_static("attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Attachment,
            parameters: vec![
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    vec![0xc2, 0xa3, 0x20, b'a', b'n', b'd', 0x20,
                         0xe2, 0x82, 0xac, 0x20, b'r', b'a', b't', b'e', b's']) ]
        };
        assert_eq!(a, b);
    }

    #[test]
    fn test_display() {
        let as_string = "attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates";
        let a = HeaderValue::from_static(as_string);
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!(as_string, display_rendered);

        let a = HeaderValue::from_static("attachment; filename*=UTF-8''black%20and%20white.csv");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered);

        let a = HeaderValue::from_static("attachment; filename=colourful.csv");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered);
    }
}
<commit_msg>content_disposition: remove unnecessary allocations<commit_after>// # References
//
// "The Content-Disposition Header Field" https://www.ietf.org/rfc/rfc2183.txt
// "The Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)" https://www.ietf.org/rfc/rfc6266.txt
// "Returning Values from Forms: multipart/form-data" https://www.ietf.org/rfc/rfc2388.txt
// Browser conformance tests at: http://greenbytes.de/tech/tc2231/
// IANA assignment: http://www.iana.org/assignments/cont-disp/cont-disp.xhtml

use language_tags::LanguageTag;
use header;
use header::{Header, IntoHeaderValue, Writer};
use header::shared::Charset;

use std::fmt::{self, Write};

/// The implied disposition of the content of the HTTP body.
#[derive(Clone, Debug, PartialEq)]
pub enum DispositionType {
    /// Inline implies default processing
    Inline,
    /// Attachment implies that the recipient should prompt the user to save the response locally,
    /// rather than process it normally (as per its media type).
    Attachment,
    /// Extension type.  Should be handled by recipients the same way as Attachment
    Ext(String)
}

/// A parameter to the disposition type.
#[derive(Clone, Debug, PartialEq)]
pub enum DispositionParam {
    /// A Filename consisting of a Charset, an optional LanguageTag, and finally a sequence of
    /// bytes representing the filename
    Filename(Charset, Option<LanguageTag>, Vec<u8>),
    /// Extension type consisting of token and value.  Recipients should ignore unrecognized
    /// parameters.
    Ext(String, String)
}

/// A `Content-Disposition` header, (re)defined in [RFC6266](https://tools.ietf.org/html/rfc6266).
///
/// The Content-Disposition response header field is used to convey
/// additional information about how to process the response payload, and
/// also can be used to attach additional metadata, such as the filename
/// to use when saving the response payload locally.
///
/// # ABNF

/// ```text
/// content-disposition = "Content-Disposition" ":"
///                       disposition-type *( ";" disposition-parm )
///
/// disposition-type    = "inline" | "attachment" | disp-ext-type
///                       ; case-insensitive
///
/// disp-ext-type       = token
///
/// disposition-parm    = filename-parm | disp-ext-parm
///
/// filename-parm       = "filename" "=" value
///                     | "filename*" "=" ext-value
///
/// disp-ext-parm       = token "=" value
///                     | ext-token "=" ext-value
///
/// ext-token           = <the characters in token, followed by "*">
/// ```
///
/// # Example
///
/// ```
/// use actix_web::http::header::{ContentDisposition, DispositionType, DispositionParam, Charset};
///
/// let cd1 = ContentDisposition {
///     disposition: DispositionType::Attachment,
///     parameters: vec![DispositionParam::Filename(
///       Charset::Iso_8859_1, // The character set for the bytes of the filename
///       None, // The optional language tag (see `language-tag` crate)
///       b"\xa9 Copyright 1989.txt".to_vec() // the actual bytes of the filename
///     )]
/// };
///
/// let cd2 = ContentDisposition {
///     disposition: DispositionType::Inline,
///     parameters: vec![DispositionParam::Filename(
///       Charset::Ext("UTF-8".to_owned()),
///       None,
///       "\u{2764}".as_bytes().to_vec()
///     )]
/// };
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct ContentDisposition {
    /// The disposition
    pub disposition: DispositionType,
    /// Disposition parameters
    pub parameters: Vec<DispositionParam>,
}
impl ContentDisposition {
    /// Parse a raw Content-Disposition header value
    pub fn from_raw(hv: &header::HeaderValue) -> Result<Self, ::error::ParseError> {
        header::from_one_raw_str(Some(hv)).and_then(|s: String| {
            let mut sections = s.split(';');
            let disposition = match sections.next() {
                Some(s) => s.trim(),
                None => return Err(::error::ParseError::Header),
            };

            let mut cd = ContentDisposition {
                disposition: if disposition.eq_ignore_ascii_case("inline") {
                    DispositionType::Inline
                } else if disposition.eq_ignore_ascii_case("attachment") {
                    DispositionType::Attachment
                } else {
                    DispositionType::Ext(disposition.to_owned())
                },
                parameters: Vec::new(),
            };

            for section in sections {
                let mut parts = section.splitn(2, '=');

                let key = if let Some(key) = parts.next() {
                    key.trim()
                } else {
                    return Err(::error::ParseError::Header);
                };

                let val = if let Some(val) = parts.next() {
                    val.trim()
                } else {
                    return Err(::error::ParseError::Header);
                };

                cd.parameters.push(
                    if key.eq_ignore_ascii_case("filename") {
                        DispositionParam::Filename(
                            Charset::Ext("UTF-8".to_owned()), None,
                            val.trim_matches('"').as_bytes().to_owned())
                    } else if key.eq_ignore_ascii_case("filename*") {
                        let extended_value = try!(header::parse_extended_value(val));
                        DispositionParam::Filename(extended_value.charset, extended_value.language_tag, extended_value.value)
                    } else {
                        DispositionParam::Ext(key.to_owned(), val.trim_matches('"').to_owned())
                    }
                );
            }

            Ok(cd)
        })
    }
}

impl IntoHeaderValue for ContentDisposition {
    type Error = header::InvalidHeaderValueBytes;

    fn try_into(self) -> Result<header::HeaderValue, Self::Error> {
        let mut writer = Writer::new();
        let _ = write!(&mut writer, "{}", self);
        header::HeaderValue::from_shared(writer.take())
    }
}

impl Header for ContentDisposition {
    fn name() -> header::HeaderName {
        header::CONTENT_DISPOSITION
    }

    fn parse<T: ::HttpMessage>(msg: &T) -> Result<Self, ::error::ParseError> {
        if let Some(h) = msg.headers().get(Self::name()) {
            Self::from_raw(&h)
        } else {
            Err(::error::ParseError::Header)
        }
    }
}

impl fmt::Display for ContentDisposition {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.disposition {
            DispositionType::Inline => try!(write!(f, "inline")),
            DispositionType::Attachment => try!(write!(f, "attachment")),
            DispositionType::Ext(ref s) => try!(write!(f, "{}", s)),
        }
        for param in &self.parameters {
            match *param {
                DispositionParam::Filename(ref charset, ref opt_lang, ref bytes) => {
                    let mut use_simple_format: bool = false;
                    if opt_lang.is_none() {
                        if let Charset::Ext(ref ext) = *charset {
                            if ext.eq_ignore_ascii_case("utf-8") {
                                use_simple_format = true;
                            }
                        }
                    }
                    if use_simple_format {
                        use std::str;
                        try!(write!(f, "; filename=\"{}\"",
                                    match str::from_utf8(bytes) {
                                        Ok(s) => s,
                                        Err(_) => return Err(fmt::Error),
                                    }));
                    } else {
                        try!(write!(f, "; filename*={}'", charset));
                        if let Some(ref lang) = *opt_lang {
                            try!(write!(f, "{}", lang));
                        };
                        try!(write!(f, "'"));
                        try!(header::http_percent_encode(f, bytes))
                    }
                },
                DispositionParam::Ext(ref k, ref v) => try!(write!(f, "; {}=\"{}\"", k, v)),
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::{ContentDisposition,DispositionType,DispositionParam};
    use header::HeaderValue;
    use header::shared::Charset;
    #[test]
    fn test_from_raw() {
        assert!(ContentDisposition::from_raw(&HeaderValue::from_static("")).is_err());

        let a = HeaderValue::from_static("form-data; dummy=3; name=upload; filename=\"sample.png\"");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_owned()),
            parameters: vec![
                DispositionParam::Ext("dummy".to_owned(), "3".to_owned()),
                DispositionParam::Ext("name".to_owned(), "upload".to_owned()),
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    "sample.png".bytes().collect()) ]
        };
        assert_eq!(a, b);

        let a = HeaderValue::from_static("attachment; filename=\"image.jpg\"");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Attachment,
            parameters: vec![
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    "image.jpg".bytes().collect()) ]
        };
        assert_eq!(a, b);

        let a = HeaderValue::from_static("attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let b = ContentDisposition {
            disposition: DispositionType::Attachment,
            parameters: vec![
                DispositionParam::Filename(
                    Charset::Ext("UTF-8".to_owned()),
                    None,
                    vec![0xc2, 0xa3, 0x20, b'a', b'n', b'd', 0x20,
                         0xe2, 0x82, 0xac, 0x20, b'r', b'a', b't', b'e', b's']) ]
        };
        assert_eq!(a, b);
    }

    #[test]
    fn test_display() {
        let as_string = "attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates";
        let a = HeaderValue::from_static(as_string);
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!(as_string, display_rendered);

        let a = HeaderValue::from_static("attachment; filename*=UTF-8''black%20and%20white.csv");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered);

        let a = HeaderValue::from_static("attachment; filename=colourful.csv");
        let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
        let display_rendered = format!("{}",a);
        assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered);
    }
}
<|endoftext|>


<commit_before>
extern crate nix;
extern crate rs9p;
extern crate env_logger;
extern crate filetime;

use std::fs;
use std::path::PathBuf;
use std::io::{Seek, SeekFrom, Read, Write};
use std::os::unix::prelude::*;
use rs9p::*;
use rs9p::srv::{Fid, Filesystem};
use self::filetime::FileTime;

#[macro_use]
mod utils;
use utils::*;

struct UnpfsFid {
    realpath: PathBuf,
    file: Option<fs::File>
}

impl UnpfsFid {
    fn new<P: AsRef<std::ffi::OsStr> + ?Sized>(path: &P) -> UnpfsFid {
        UnpfsFid { realpath: PathBuf::from(path), file: None, }
    }
}

struct Unpfs {
    realroot: PathBuf,
}

impl Filesystem for Unpfs {
    type Fid = UnpfsFid;

    fn rattach(&mut self, fid: &mut Fid<Self::Fid>, _afid: Option<&mut Fid<Self::Fid>>, _uname: &str, _aname: &str, _n_uname: u32) -> Result<Fcall> {
        fid.aux = Some(UnpfsFid::new(&self.realroot));
        Ok(Fcall::Rattach { qid: get_qid(&self.realroot)? })
    }

    fn rwalk(&mut self, fid: &mut Fid<Self::Fid>, newfid: &mut Fid<Self::Fid>, wnames: &[String]) -> Result<Fcall> {
        let mut wqids = Vec::new();
        let mut path = fid.aux().realpath.clone();

        for (i, name) in wnames.iter().enumerate() {
            path.push(name);
            let qid = match get_qid(&path) {
                Ok(qid) => qid,
                Err(e) => if i == 0 { return Err(e) } else { break },
            };
            wqids.push(qid);
        }
        newfid.aux = Some(UnpfsFid::new(&path));

        Ok(Fcall::Rwalk { wqids: wqids })
    }

    fn rgetattr(&mut self, fid: &mut Fid<Self::Fid>, req_mask: GetattrMask) -> Result<Fcall> {
        let attr = fs::symlink_metadata(&fid.aux().realpath)?;
        Ok(Fcall::Rgetattr {
            valid: req_mask,
            qid: qid_from_attr(&attr),
            stat: From::from(attr)
        })
    }

    fn rsetattr(&mut self, fid: &mut Fid<Self::Fid>, valid: SetattrMask, stat: &SetAttr) -> Result<Fcall> {
        let filepath = &fid.aux().realpath;
        if valid.contains(setattr::MODE) {
            fs::set_permissions(filepath, PermissionsExt::from_mode(stat.mode))?;
        }
        if valid.intersects(setattr::UID | setattr::GID) {
            let uid = if valid.contains(setattr::UID) { Some(stat.uid) } else { None };
            let gid = if valid.contains(setattr::GID) { Some(stat.gid) } else { None };
            nix::unistd::chown(filepath, uid, gid)?;
        }
        if valid.contains(setattr::SIZE) {
            let _ = fs::File::open(filepath)?.set_len(stat.size);
        }
        if valid.intersects(setattr::ATIME_SET | setattr::MTIME_SET) {
            let atime = if valid.contains(setattr::ATIME_SET) {
                FileTime::from_seconds_since_1970(stat.atime.sec, stat.atime.nsec as u32)
            } else { FileTime::from_last_access_time(&fs::metadata(filepath)?) };
            let mtime = if valid.contains(setattr::MTIME_SET) {
                FileTime::from_seconds_since_1970(stat.mtime.sec, stat.mtime.nsec as u32)
            } else { FileTime::from_last_modification_time(&fs::metadata(filepath)?) };
            filetime::set_file_times(filepath, atime, mtime)?
        }
        Ok(Fcall::Rsetattr)
    }

    fn rreadlink(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        let link = fs::read_link(&fid.aux().realpath)?;
        Ok(Fcall::Rreadlink { target: link.to_string_lossy().into_owned() })
    }

    fn rreaddir(&mut self, fid: &mut Fid<Self::Fid>, off: u64, count: u32) -> Result<Fcall> {
        let mut dirents = DirEntryData::new();

        let offset = if off == 0 {
            dirents.push(get_dirent_from(&".", 0)?);
            dirents.push(get_dirent_from(&"..", 1)?);
            off
        } else { off - 1 } as usize;

        let entries = fs::read_dir(&fid.aux().realpath)?;
        for (i, entry) in entries.enumerate().skip(offset) {
            let dirent = get_dirent(&entry?, 2 + i as u64)?;
            if dirents.size() + dirent.size() > count {
                break;
            }
            dirents.push(dirent);
        }

        Ok(Fcall::Rreaddir { data: dirents })
    }

    fn rlopen(&mut self, fid: &mut Fid<Self::Fid>, flags: u32) -> Result<Fcall> {
        let qid = get_qid(&fid.aux().realpath)?;

        if !qid.typ.contains(qt::DIR) {
            let oflags = nix::fcntl::OFlag::from_bits_truncate(flags as i32);
            let omode = nix::sys::stat::Mode::from_bits_truncate(0);
            let fd = nix::fcntl::open(&fid.aux().realpath, oflags, omode)?;
            fid.aux_mut().file = Some(unsafe { fs::File::from_raw_fd(fd) });
        }

        Ok(Fcall::Rlopen { qid: qid, iounit: 0 })
    }

    fn rlcreate(&mut self, fid: &mut Fid<Self::Fid>, name: &str, flags: u32, mode: u32, _gid: u32) -> Result<Fcall> {
        let path = fid.aux().realpath.join(name);
        let oflags = nix::fcntl::OFlag::from_bits_truncate(flags as i32);
        let omode = nix::sys::stat::Mode::from_bits_truncate(mode);
        let fd = nix::fcntl::open(&path, oflags, omode)?;

        fid.aux = Some(UnpfsFid::new(&path));
        fid.aux_mut().file = Some(unsafe { fs::File::from_raw_fd(fd) });

        Ok(Fcall::Rlcreate { qid: get_qid(&path)?, iounit: 0 })
    }

    fn rread(&mut self, fid: &mut Fid<Self::Fid>, offset: u64, count: u32) -> Result<Fcall> {
        let file = fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?;
        file.seek(SeekFrom::Start(offset))?;

        let mut buf = create_buffer(count as usize);
        let bytes = file.read(&mut buf[..])?;
        buf.truncate(bytes);

        Ok(Fcall::Rread { data: Data(buf) })
    }

    fn rwrite(&mut self, fid: &mut Fid<Self::Fid>, offset: u64, data: &Data) -> Result<Fcall> {
        let file = fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?;
        file.seek(SeekFrom::Start(offset))?;
        Ok(Fcall::Rwrite { count: file.write(&data.0)? as u32 })
    }

    fn rmkdir(&mut self, dfid: &mut Fid<Self::Fid>, name: &str, _mode: u32, _gid: u32) -> Result<Fcall> {
        let path = dfid.aux().realpath.join(name);
        fs::create_dir(&path)?;
        Ok(Fcall::Rmkdir { qid: get_qid(&path)? })
    }

    fn rrenameat(&mut self, olddir: &mut Fid<Self::Fid>, oldname: &str, newdir: &mut Fid<Self::Fid>, newname: &str) -> Result<Fcall> {
        let oldpath = olddir.aux().realpath.join(oldname);
        let newpath = newdir.aux().realpath.join(newname);
        fs::rename(&oldpath, &newpath)?;
        Ok(Fcall::Rrenameat)
    }

    fn runlinkat(&mut self, dirfid: &mut Fid<Self::Fid>, name: &str, _flags: u32) -> Result<Fcall> {
        let path = dirfid.aux().realpath.join(name);
        match fs::symlink_metadata(&path)? {
            ref attr if attr.is_dir() => fs::remove_dir(&path)?,
            _ => fs::remove_file(&path)?,
        };
        Ok(Fcall::Runlinkat)
    }

    fn rfsync(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?.sync_all()?;
        Ok(Fcall::Rfsync)
    }

    fn rclunk(&mut self, _: &mut Fid<Self::Fid>) -> Result<Fcall> {
        Ok(Fcall::Rclunk)
    }

    fn rstatfs(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        let fs = nix::sys::statvfs::vfs::Statvfs::for_path(&fid.aux().realpath)?;
        Ok(Fcall::Rstatfs { statfs: From::from(fs) })
    }
}

fn unpfs_main(args: Vec<String>) -> rs9p::Result<i32> {
    if args.len() < 3 {
        println!("Usage: {} proto!address!port mountpoint", args[0]);
        println!("  where: proto = tcp | unix");
        return Ok(-1);
    }

    let (addr, mountpoint) = (&args[1], PathBuf::from(&args[2]));
    if !fs::metadata(&mountpoint)?.is_dir() {
        return res!(io_err!(Other, "mount point must be a directory"));
    }

    println!("[*] Ready to accept clients: {}", addr);
    rs9p::srv_spawn(Unpfs { realroot: mountpoint }, addr).and(Ok(0))
}

fn main() {
    env_logger::init().unwrap();
    let args = std::env::args().collect();
    let exit_code = unpfs_main(args)
        .unwrap_or_else(|e| { println!("Error: {}", e); -1 });
    std::process::exit(exit_code);
}
<commit_msg>Remove &s<commit_after>
extern crate nix;
extern crate rs9p;
extern crate env_logger;
extern crate filetime;

use std::fs;
use std::path::PathBuf;
use std::io::{Seek, SeekFrom, Read, Write};
use std::os::unix::prelude::*;
use rs9p::*;
use rs9p::srv::{Fid, Filesystem};
use self::filetime::FileTime;

#[macro_use]
mod utils;
use utils::*;

struct UnpfsFid {
    realpath: PathBuf,
    file: Option<fs::File>
}

impl UnpfsFid {
    fn new<P: AsRef<std::ffi::OsStr> + ?Sized>(path: &P) -> UnpfsFid {
        UnpfsFid { realpath: PathBuf::from(path), file: None, }
    }
}

struct Unpfs {
    realroot: PathBuf,
}

impl Filesystem for Unpfs {
    type Fid = UnpfsFid;

    fn rattach(&mut self, fid: &mut Fid<Self::Fid>, _afid: Option<&mut Fid<Self::Fid>>, _uname: &str, _aname: &str, _n_uname: u32) -> Result<Fcall> {
        fid.aux = Some(UnpfsFid::new(&self.realroot));
        Ok(Fcall::Rattach { qid: get_qid(&self.realroot)? })
    }

    fn rwalk(&mut self, fid: &mut Fid<Self::Fid>, newfid: &mut Fid<Self::Fid>, wnames: &[String]) -> Result<Fcall> {
        let mut wqids = Vec::new();
        let mut path = fid.aux().realpath.clone();

        for (i, name) in wnames.iter().enumerate() {
            path.push(name);
            let qid = match get_qid(&path) {
                Ok(qid) => qid,
                Err(e) => if i == 0 { return Err(e) } else { break },
            };
            wqids.push(qid);
        }
        newfid.aux = Some(UnpfsFid::new(&path));

        Ok(Fcall::Rwalk { wqids: wqids })
    }

    fn rgetattr(&mut self, fid: &mut Fid<Self::Fid>, req_mask: GetattrMask) -> Result<Fcall> {
        let attr = fs::symlink_metadata(&fid.aux().realpath)?;
        Ok(Fcall::Rgetattr {
            valid: req_mask,
            qid: qid_from_attr(&attr),
            stat: From::from(attr)
        })
    }

    fn rsetattr(&mut self, fid: &mut Fid<Self::Fid>, valid: SetattrMask, stat: &SetAttr) -> Result<Fcall> {
        let filepath = &fid.aux().realpath;
        if valid.contains(setattr::MODE) {
            fs::set_permissions(filepath, PermissionsExt::from_mode(stat.mode))?;
        }
        if valid.intersects(setattr::UID | setattr::GID) {
            let uid = if valid.contains(setattr::UID) { Some(stat.uid) } else { None };
            let gid = if valid.contains(setattr::GID) { Some(stat.gid) } else { None };
            nix::unistd::chown(filepath, uid, gid)?;
        }
        if valid.contains(setattr::SIZE) {
            let _ = fs::File::open(filepath)?.set_len(stat.size);
        }
        if valid.intersects(setattr::ATIME_SET | setattr::MTIME_SET) {
            let atime = if valid.contains(setattr::ATIME_SET) {
                FileTime::from_seconds_since_1970(stat.atime.sec, stat.atime.nsec as u32)
            } else { FileTime::from_last_access_time(&fs::metadata(filepath)?) };
            let mtime = if valid.contains(setattr::MTIME_SET) {
                FileTime::from_seconds_since_1970(stat.mtime.sec, stat.mtime.nsec as u32)
            } else { FileTime::from_last_modification_time(&fs::metadata(filepath)?) };
            filetime::set_file_times(filepath, atime, mtime)?
        }
        Ok(Fcall::Rsetattr)
    }

    fn rreadlink(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        let link = fs::read_link(&fid.aux().realpath)?;
        Ok(Fcall::Rreadlink { target: link.to_string_lossy().into_owned() })
    }

    fn rreaddir(&mut self, fid: &mut Fid<Self::Fid>, off: u64, count: u32) -> Result<Fcall> {
        let mut dirents = DirEntryData::new();

        let offset = if off == 0 {
            dirents.push(get_dirent_from(".", 0)?);
            dirents.push(get_dirent_from("..", 1)?);
            off
        } else { off - 1 } as usize;

        let entries = fs::read_dir(&fid.aux().realpath)?;
        for (i, entry) in entries.enumerate().skip(offset) {
            let dirent = get_dirent(&entry?, 2 + i as u64)?;
            if dirents.size() + dirent.size() > count {
                break;
            }
            dirents.push(dirent);
        }

        Ok(Fcall::Rreaddir { data: dirents })
    }

    fn rlopen(&mut self, fid: &mut Fid<Self::Fid>, flags: u32) -> Result<Fcall> {
        let qid = get_qid(&fid.aux().realpath)?;

        if !qid.typ.contains(qt::DIR) {
            let oflags = nix::fcntl::OFlag::from_bits_truncate(flags as i32);
            let omode = nix::sys::stat::Mode::from_bits_truncate(0);
            let fd = nix::fcntl::open(&fid.aux().realpath, oflags, omode)?;
            fid.aux_mut().file = Some(unsafe { fs::File::from_raw_fd(fd) });
        }

        Ok(Fcall::Rlopen { qid: qid, iounit: 0 })
    }

    fn rlcreate(&mut self, fid: &mut Fid<Self::Fid>, name: &str, flags: u32, mode: u32, _gid: u32) -> Result<Fcall> {
        let path = fid.aux().realpath.join(name);
        let oflags = nix::fcntl::OFlag::from_bits_truncate(flags as i32);
        let omode = nix::sys::stat::Mode::from_bits_truncate(mode);
        let fd = nix::fcntl::open(&path, oflags, omode)?;

        fid.aux = Some(UnpfsFid::new(&path));
        fid.aux_mut().file = Some(unsafe { fs::File::from_raw_fd(fd) });

        Ok(Fcall::Rlcreate { qid: get_qid(&path)?, iounit: 0 })
    }

    fn rread(&mut self, fid: &mut Fid<Self::Fid>, offset: u64, count: u32) -> Result<Fcall> {
        let file = fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?;
        file.seek(SeekFrom::Start(offset))?;

        let mut buf = create_buffer(count as usize);
        let bytes = file.read(&mut buf[..])?;
        buf.truncate(bytes);

        Ok(Fcall::Rread { data: Data(buf) })
    }

    fn rwrite(&mut self, fid: &mut Fid<Self::Fid>, offset: u64, data: &Data) -> Result<Fcall> {
        let file = fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?;
        file.seek(SeekFrom::Start(offset))?;
        Ok(Fcall::Rwrite { count: file.write(&data.0)? as u32 })
    }

    fn rmkdir(&mut self, dfid: &mut Fid<Self::Fid>, name: &str, _mode: u32, _gid: u32) -> Result<Fcall> {
        let path = dfid.aux().realpath.join(name);
        fs::create_dir(&path)?;
        Ok(Fcall::Rmkdir { qid: get_qid(&path)? })
    }

    fn rrenameat(&mut self, olddir: &mut Fid<Self::Fid>, oldname: &str, newdir: &mut Fid<Self::Fid>, newname: &str) -> Result<Fcall> {
        let oldpath = olddir.aux().realpath.join(oldname);
        let newpath = newdir.aux().realpath.join(newname);
        fs::rename(&oldpath, &newpath)?;
        Ok(Fcall::Rrenameat)
    }

    fn runlinkat(&mut self, dirfid: &mut Fid<Self::Fid>, name: &str, _flags: u32) -> Result<Fcall> {
        let path = dirfid.aux().realpath.join(name);
        match fs::symlink_metadata(&path)? {
            ref attr if attr.is_dir() => fs::remove_dir(&path)?,
            _ => fs::remove_file(&path)?,
        };
        Ok(Fcall::Runlinkat)
    }

    fn rfsync(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        fid.aux_mut().file.as_mut().ok_or(INVALID_FID!())?.sync_all()?;
        Ok(Fcall::Rfsync)
    }

    fn rclunk(&mut self, _: &mut Fid<Self::Fid>) -> Result<Fcall> {
        Ok(Fcall::Rclunk)
    }

    fn rstatfs(&mut self, fid: &mut Fid<Self::Fid>) -> Result<Fcall> {
        let fs = nix::sys::statvfs::vfs::Statvfs::for_path(&fid.aux().realpath)?;
        Ok(Fcall::Rstatfs { statfs: From::from(fs) })
    }
}

fn unpfs_main(args: Vec<String>) -> rs9p::Result<i32> {
    if args.len() < 3 {
        println!("Usage: {} proto!address!port mountpoint", args[0]);
        println!("  where: proto = tcp | unix");
        return Ok(-1);
    }

    let (addr, mountpoint) = (&args[1], PathBuf::from(&args[2]));
    if !fs::metadata(&mountpoint)?.is_dir() {
        return res!(io_err!(Other, "mount point must be a directory"));
    }

    println!("[*] Ready to accept clients: {}", addr);
    rs9p::srv_spawn(Unpfs { realroot: mountpoint }, addr).and(Ok(0))
}

fn main() {
    env_logger::init().unwrap();
    let args = std::env::args().collect();
    let exit_code = unpfs_main(args)
        .unwrap_or_else(|e| { println!("Error: {}", e); -1 });
    std::process::exit(exit_code);
}
<|endoftext|>


<commit_before>// Std
use std::io::Write;

// Internal
use app::parser::Parser;
use args::{ArgSettings, OptBuilder};
use shell::Shell;

macro_rules! w {
    ($buf:expr, $to_w:expr) => {
        match $buf.write_all($to_w) {
            Ok(..) => (),
            Err(..) => panic!("Failed to write to file completions file"),
        }
    };
}

pub struct ComplGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> ComplGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self {
        ComplGen { p: p }
    }

    pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
        match for_shell {
            Shell::Bash => self.gen_bash(buf),
            Shell::Fish => self.gen_fish(buf),
        }
    }

    fn gen_bash<W: Write>(&self, buf: &mut W) {
        w!(buf,
           format!("_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{name}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {name})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} {name}
",
                   name = self.p.meta.bin_name.as_ref().unwrap(),
                   name_opts = self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()),
                   name_opts_details =
                       self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()),
                   subcmds = self.all_subcommands(),
                   subcmd_details = self.subcommand_details())
               .as_bytes());
    }

    fn all_subcommands(&self) -> String {
        let mut subcmds = String::new();
        let scs = get_all_subcommands(self.p);

        for sc in &scs {
            subcmds = format!("{}
            {name})
                cmd+=\"_{name}\"
                ;;",
                              subcmds,
                              name = sc.replace("-", "_"));
        }

        subcmds
    }

    fn subcommand_details(&self) -> String {
        let mut subcmd_dets = String::new();
        let mut scs = get_all_subcommand_paths(self.p, true);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmd_dets = format!("{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;",
                                  subcmd_dets,
                                  subcmd = sc.replace("-", "_"),
                                  sc_opts = self.all_options_for_path(&*sc),
                                  level = sc.split("_").map(|_| 1).fold(0, |acc, n| acc + n),
                                  opts_details = self.option_details_for_path(&*sc));
        }

        subcmd_dets
    }

    fn all_options_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split('_').skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands
                .iter()
                .find(|s| {
                    s.p.meta.name == sc ||
                    (s.p.meta.aliases.is_some() &&
                     s.p
                        .meta
                        .aliases
                        .as_ref()
                        .unwrap()
                        .iter()
                        .any(|&(n, _)| n == sc))
                })
                .unwrap()
                .p;
        }
        let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
        opts = format!("{} {}",
                       opts,
                       p.long_list
                           .iter()
                           .fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
        opts = format!("{} {}",
                       opts,
                       p.positionals
                           .values()
                           .fold(String::new(), |acc, p| format!("{} {}", acc, p)));
        opts = format!("{} {}",
                       opts,
                       p.subcommands
                           .iter()
                           .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
        for sc in &p.subcommands {
            if let Some(ref aliases) = sc.p.meta.aliases {
                opts = format!("{} {}",
                               opts,
                               aliases.iter()
                                   .map(|&(n, _)| n)
                                   .fold(String::new(), |acc, a| format!("{} {}", acc, a)));
            }
        }
        opts
    }

    fn option_details_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split('_').skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands
                .iter()
                .find(|s| {
                    s.p.meta.name == sc ||
                    (s.p.meta.aliases.is_some() &&
                     s.p
                        .meta
                        .aliases
                        .as_ref()
                        .unwrap()
                        .iter()
                        .any(|&(n, _)| n == sc))
                })
                .unwrap()
                .p;
        }
        let mut opts = String::new();
        for o in &p.opts {
            if let Some(l) = o.long {
                opts = format!("{}
                --{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                               opts,
                               l,
                               vals_for(o));
            }
            if let Some(s) = o.short {
                opts = format!("{}
                    -{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                               opts,
                               s,
                               vals_for(o));
            }
        }
        opts
    }

    fn gen_fish<W: Write>(&self, buf: &mut W) {
        let command = self.p.meta.bin_name.as_ref().unwrap();
        let subcommands: Vec<_> = get_all_subcommands(self.p);
        let has_subcommands = subcommands.len() > 1;

        // function to detect subcommand
        let detect_subcommand_function = if has_subcommands {
            format!(
r#"function __fish_{}_no_subcommand --description "Test if there isn't given a subcommand"
    for i in (commandline -opc)
        if contains -- $i {}
            return 1
        end
    end
    return 0
end
"#, command, subcommands.join(" "))
        } else {
            "".to_string()
        };

        let mut buffer = detect_subcommand_function;
        gen_fish_inner(command, self, vec![], &mut buffer, has_subcommands);
        w!(buf, buffer.as_bytes());
    }
}

pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        let mut ret = vec![p.meta.name.clone()];
        if let Some(ref aliases) = p.meta.aliases {
            for &(n, _) in aliases {
                ret.push(n.to_owned());
            }
        }
        return ret;
    }
    for sc in &p.subcommands {
        if let Some(ref aliases) = sc.p.meta.aliases {
            for &(n, _) in aliases {
                subcmds.push(n.to_owned());
            }
        }
        subcmds.push(sc.p.meta.name.clone());
    }
    for sc_v in p.subcommands.iter().map(|s| get_all_subcommands(&s.p)) {
        subcmds.extend(sc_v);
    }
    subcmds.sort();
    subcmds.dedup();
    subcmds
}

pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        if !first {
            let name = &*p.meta.name;
            let path = p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
            let mut ret = vec![path.clone()];
            if let Some(ref aliases) = p.meta.aliases {
                for &(n, _) in aliases {
                    ret.push(path.replace(name, n));
                }
            }
            return ret;
        }
        return vec![];
    }
    for sc in &p.subcommands {
        let name = &*sc.p.meta.name;
        let path = sc.p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
        subcmds.push(path.clone());
        if let Some(ref aliases) = sc.p.meta.aliases {
            for &(n, _) in aliases {
                subcmds.push(path.replace(name, n));
            }
        }
    }
    for sc_v in p.subcommands.iter().map(|s| get_all_subcommand_paths(&s.p, false)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

fn vals_for(o: &OptBuilder) -> String {
    use args::AnyArg;
    let mut ret = String::new();
    let mut needs_quotes = true;
    if let Some(vals) = o.possible_vals() {
        needs_quotes = false;
        ret = format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "));
    } else if let Some(vec) = o.val_names() {
        let mut it = vec.iter().peekable();
        while let Some((_, val)) = it.next() {
            ret = format!("{}<{}>{}",
                          ret,
                          val,
                          if it.peek().is_some() { " " } else { "" });
        }
        let num = vec.len();
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else if let Some(num) = o.num_vals() {
        let mut it = (0..num).peekable();
        while let Some(_) = it.next() {
            ret = format!("{}<{}>{}",
                          ret,
                          o.name(),
                          if it.peek().is_some() { " " } else { "" });
        }
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else {
        ret = format!("<{}>", o.name());
        if o.is_set(ArgSettings::Multiple) {
            ret = format!("{}...", ret);
        }
    }
    if needs_quotes {
        ret = format!("\"{}\"", ret);
    }
    ret
}

fn gen_fish_inner(root_command: &str,
                  comp_gen: &ComplGen,
                  parent_cmds: Vec<&String>,
                  buffer: &mut String,
                  has_no_subcommand_fn: bool) {
    // example :
    //
    // complete
    //      -c {command}
    //      -d "{description}"
    //      -s {short}
    //      -l {long}
    //      -a "{possible_arguments}"
    //      -r # if require parameter
    //      -f # don't use file completion
    //      -n "__fish_seen_subcommand_from install" # complete for subcommand "install"

    let command = &comp_gen.p.meta.name;
    let subcommands: Vec<_> = get_all_subcommands(comp_gen.p);

    for option in &comp_gen.p.opts {
        let mut template = format!("complete -c {}", root_command);
        if !parent_cmds.is_empty() {
            template.push_str(format!(" -n '__fish_seen_subcommand_from {}'", command).as_str());
        } else if has_no_subcommand_fn {
            template.push_str(format!(" -n '__fish_{}_no_subcommand'",
                                      comp_gen.p.meta.bin_name.as_ref().unwrap())
                .as_str());
        }
        if let Some(data) = option.short {
            template.push_str(format!(" -s {}", data).as_str());
        }
        if let Some(data) = option.long {
            template.push_str(format!(" -l {}", data).as_str());
        }
        if let Some(data) = option.help {
            template.push_str(format!(" -d '{}'", data).as_str());
        }
        if let Some(ref data) = option.possible_vals {
            template.push_str(format!(" -r -f -a '{}'", data.join(" ")).as_str());
        }
        buffer.push_str(template.as_str());
        buffer.push_str("\n");
    }

    for flag in &comp_gen.p.flags {
        let mut template = format!("complete -c {}", root_command);
        if !parent_cmds.is_empty() {
            template.push_str(format!(" -n '__fish_seen_subcommand_from {}'", command).as_str());
        } else if has_no_subcommand_fn {
            template.push_str(format!(" -n '__fish_{}_no_subcommand'",
                                      comp_gen.p.meta.bin_name.as_ref().unwrap())
                .as_str());
        }
        if let Some(data) = flag.short {
            template.push_str(format!(" -s {}", data).as_str());
        }
        if let Some(data) = flag.long {
            template.push_str(format!(" -l {}", data).as_str());
        }
        if let Some(data) = flag.help {
            template.push_str(format!(" -d '{}'", data).as_str());
        }
        buffer.push_str(template.as_str());
        buffer.push_str("\n");
    }

    if subcommands.len() > 1 {
        for subcommand in subcommands {
            let mut template = format!("complete -c {}", root_command);
            if !parent_cmds.is_empty() {
                template.push_str(format!(" -n '__fish_seen_subcommand_from {}'",
                                          subcommand)
                    .as_str());
            } else if has_no_subcommand_fn {
                template.push_str(format!(" -n '__fish_{}_no_subcommand'",
                                          comp_gen.p.meta.bin_name.as_ref().unwrap())
                    .as_str());
            }
            template.push_str(" -f");
            template.push_str(format!(" -a '{}'", subcommand).as_str());
            buffer.push_str(template.as_str());
            buffer.push_str("\n");
        }
    }

    // generate options of subcommands
    for subcommand in &comp_gen.p.subcommands {
        let sub_comp_gen = ComplGen::new(&subcommand.p);
        let mut sub_parent_cmds = parent_cmds.clone();
        sub_parent_cmds.push(command);
        gen_fish_inner(root_command,
                       &sub_comp_gen,
                       sub_parent_cmds,
                       buffer,
                       has_no_subcommand_fn);
    }
}
<commit_msg>fix(Completions): fish completions for nested subcommands<commit_after>// Std
use std::io::Write;

// Internal
use app::parser::Parser;
use args::{ArgSettings, OptBuilder};
use shell::Shell;

macro_rules! w {
    ($buf:expr, $to_w:expr) => {
        match $buf.write_all($to_w) {
            Ok(..) => (),
            Err(..) => panic!("Failed to write to file completions file"),
        }
    };
}

pub struct ComplGen<'a, 'b>
    where 'a: 'b
{
    p: &'b Parser<'a, 'b>,
}

impl<'a, 'b> ComplGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>) -> Self {
        ComplGen { p: p }
    }

    pub fn generate<W: Write>(&self, for_shell: Shell, buf: &mut W) {
        match for_shell {
            Shell::Bash => self.gen_bash(buf),
            Shell::Fish => self.gen_fish(buf),
        }
    }

    fn gen_bash<W: Write>(&self, buf: &mut W) {
        w!(buf,
           format!("_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{name}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {name})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} {name}
",
                   name = self.p.meta.bin_name.as_ref().unwrap(),
                   name_opts = self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()),
                   name_opts_details =
                       self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()),
                   subcmds = self.all_subcommands(),
                   subcmd_details = self.subcommand_details())
               .as_bytes());
    }

    fn all_subcommands(&self) -> String {
        let mut subcmds = String::new();
        let scs = get_all_subcommands(self.p);

        for sc in &scs {
            subcmds = format!("{}
            {name})
                cmd+=\"_{name}\"
                ;;",
                              subcmds,
                              name = sc.replace("-", "_"));
        }

        subcmds
    }

    fn subcommand_details(&self) -> String {
        let mut subcmd_dets = String::new();
        let mut scs = get_all_subcommand_paths(self.p, true);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmd_dets = format!("{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;",
                                  subcmd_dets,
                                  subcmd = sc.replace("-", "_"),
                                  sc_opts = self.all_options_for_path(&*sc),
                                  level = sc.split("_").map(|_| 1).fold(0, |acc, n| acc + n),
                                  opts_details = self.option_details_for_path(&*sc));
        }

        subcmd_dets
    }

    fn all_options_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split('_').skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands
                .iter()
                .find(|s| {
                    s.p.meta.name == sc ||
                    (s.p.meta.aliases.is_some() &&
                     s.p
                        .meta
                        .aliases
                        .as_ref()
                        .unwrap()
                        .iter()
                        .any(|&(n, _)| n == sc))
                })
                .unwrap()
                .p;
        }
        let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
        opts = format!("{} {}",
                       opts,
                       p.long_list
                           .iter()
                           .fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
        opts = format!("{} {}",
                       opts,
                       p.positionals
                           .values()
                           .fold(String::new(), |acc, p| format!("{} {}", acc, p)));
        opts = format!("{} {}",
                       opts,
                       p.subcommands
                           .iter()
                           .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
        for sc in &p.subcommands {
            if let Some(ref aliases) = sc.p.meta.aliases {
                opts = format!("{} {}",
                               opts,
                               aliases.iter()
                                   .map(|&(n, _)| n)
                                   .fold(String::new(), |acc, a| format!("{} {}", acc, a)));
            }
        }
        opts
    }

    fn option_details_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split('_').skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands
                .iter()
                .find(|s| {
                    s.p.meta.name == sc ||
                    (s.p.meta.aliases.is_some() &&
                     s.p
                        .meta
                        .aliases
                        .as_ref()
                        .unwrap()
                        .iter()
                        .any(|&(n, _)| n == sc))
                })
                .unwrap()
                .p;
        }
        let mut opts = String::new();
        for o in &p.opts {
            if let Some(l) = o.long {
                opts = format!("{}
                --{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                               opts,
                               l,
                               vals_for(o));
            }
            if let Some(s) = o.short {
                opts = format!("{}
                    -{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                               opts,
                               s,
                               vals_for(o));
            }
        }
        opts
    }

    fn gen_fish<W: Write>(&self, buf: &mut W) {
        let command = self.p.meta.bin_name.as_ref().unwrap();

        // function to detect subcommand
        let detect_subcommand_function =
r#"function __fish_using_command
    set cmd (commandline -opc)
    if [ (count $cmd) -eq (count $argv) ]
        for i in (seq (count $argv))
            if [ $cmd[$i] != $argv[$i] ]
                return 1
            end
        end
        return 0
    end
    return 1
end

"#.to_string();

        let mut buffer = detect_subcommand_function;
        gen_fish_inner(command, self, &command.to_string(), &mut buffer);
        w!(buf, buffer.as_bytes());
    }
}

pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        let mut ret = vec![p.meta.name.clone()];
        if let Some(ref aliases) = p.meta.aliases {
            for &(n, _) in aliases {
                ret.push(n.to_owned());
            }
        }
        return ret;
    }
    for sc in &p.subcommands {
        if let Some(ref aliases) = sc.p.meta.aliases {
            for &(n, _) in aliases {
                subcmds.push(n.to_owned());
            }
        }
        subcmds.push(sc.p.meta.name.clone());
    }
    for sc_v in p.subcommands.iter().map(|s| get_all_subcommands(&s.p)) {
        subcmds.extend(sc_v);
    }
    subcmds.sort();
    subcmds.dedup();
    subcmds
}

pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        if !first {
            let name = &*p.meta.name;
            let path = p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
            let mut ret = vec![path.clone()];
            if let Some(ref aliases) = p.meta.aliases {
                for &(n, _) in aliases {
                    ret.push(path.replace(name, n));
                }
            }
            return ret;
        }
        return vec![];
    }
    for sc in &p.subcommands {
        let name = &*sc.p.meta.name;
        let path = sc.p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
        subcmds.push(path.clone());
        if let Some(ref aliases) = sc.p.meta.aliases {
            for &(n, _) in aliases {
                subcmds.push(path.replace(name, n));
            }
        }
    }
    for sc_v in p.subcommands.iter().map(|s| get_all_subcommand_paths(&s.p, false)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

fn vals_for(o: &OptBuilder) -> String {
    use args::AnyArg;
    let mut ret = String::new();
    let mut needs_quotes = true;
    if let Some(vals) = o.possible_vals() {
        needs_quotes = false;
        ret = format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "));
    } else if let Some(vec) = o.val_names() {
        let mut it = vec.iter().peekable();
        while let Some((_, val)) = it.next() {
            ret = format!("{}<{}>{}",
                          ret,
                          val,
                          if it.peek().is_some() { " " } else { "" });
        }
        let num = vec.len();
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else if let Some(num) = o.num_vals() {
        let mut it = (0..num).peekable();
        while let Some(_) = it.next() {
            ret = format!("{}<{}>{}",
                          ret,
                          o.name(),
                          if it.peek().is_some() { " " } else { "" });
        }
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else {
        ret = format!("<{}>", o.name());
        if o.is_set(ArgSettings::Multiple) {
            ret = format!("{}...", ret);
        }
    }
    if needs_quotes {
        ret = format!("\"{}\"", ret);
    }
    ret
}

fn gen_fish_inner(root_command: &str,
                  comp_gen: &ComplGen,
                  parent_cmds: &String,
                  buffer: &mut String) {

    // example :
    //
    // complete
    //      -c {command}
    //      -d "{description}"
    //      -s {short}
    //      -l {long}
    //      -a "{possible_arguments}"
    //      -r # if require parameter
    //      -f # don't use file completion
    //      -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1"

    let basic_template = format!("complete -c {} -n '__fish_using_command {}'",
                                 root_command,
                                 parent_cmds);

    for option in &comp_gen.p.opts {
        let mut template = basic_template.clone();
        if let Some(data) = option.short {
            template.push_str(format!(" -s {}", data).as_str());
        }
        if let Some(data) = option.long {
            template.push_str(format!(" -l {}", data).as_str());
        }
        if let Some(data) = option.help {
            template.push_str(format!(" -d '{}'", data).as_str());
        }
        if let Some(ref data) = option.possible_vals {
            template.push_str(format!(" -r -f -a '{}'", data.join(" ")).as_str());
        }
        buffer.push_str(template.as_str());
        buffer.push_str("\n");
    }

    for flag in &comp_gen.p.flags {
        let mut template = basic_template.clone();
        if let Some(data) = flag.short {
            template.push_str(format!(" -s {}", data).as_str());
        }
        if let Some(data) = flag.long {
            template.push_str(format!(" -l {}", data).as_str());
        }
        if let Some(data) = flag.help {
            template.push_str(format!(" -d '{}'", data).as_str());
        }
        buffer.push_str(template.as_str());
        buffer.push_str("\n");
    }

    for subcommand in &comp_gen.p.subcommands {
        let mut template = basic_template.clone();
        template.push_str(" -f");
        template.push_str(format!(" -a '{}'", &subcommand.p.meta.name).as_str());
        buffer.push_str(template.as_str());
        buffer.push_str("\n");
    }

    // generate options of subcommands
    for subcommand in &comp_gen.p.subcommands {
        let sub_comp_gen = ComplGen::new(&subcommand.p);
        // make new "parent_cmds" for different subcommands
        let mut sub_parent_cmds = parent_cmds.clone();
        if !sub_parent_cmds.is_empty() {
            sub_parent_cmds.push_str(" ");
        }
        sub_parent_cmds.push_str(&subcommand.p.meta.name);
        gen_fish_inner(root_command,
                       &sub_comp_gen,
                       &mut sub_parent_cmds,
                       buffer);
    }
}
<|endoftext|>


<commit_before>use http::status::Status;
use http::status::Status::{Forbidden, NotFound, InternalServerError};
use nickel::{
    Continue,
    ErrorWithStatusCode,
    Halt,
    MiddlewareResult,
    NickelError,
    Request,
    Response,
};
use nickel::mimes::MediaType;
use rustdoc::html::markdown::Markdown;
use std::collections::HashMap;
use std::io::{
    BufferedReader,
    File
};

/// Render the index page.
pub fn root_handler(_request: &Request, response: &mut Response) {
    let mut data = HashMap::<&str, &str>::new();
    data.insert("name", "user");
    response.render("assets/templates/index.tpl", &data);
}

pub fn get_blog_post(request: &Request, response: &mut Response) {
    // Retrieve the post id from the request URL.
    let post_year = request.param("post_year");
    let post_id = request.param("post_id");
    let post_path = Path::new("assets/content/posts/".to_string() +
            post_year.to_string() +
            "/".to_string() +
            post_id.to_string() +
            ".md".to_string());

    // Read the post markdown from the disk.
    let content = File::open(&post_path).read_to_end().unwrap();
    let content = String::from_utf8(content).unwrap();
    let mut content = content.as_slice().split_str("\n\n");
    //let mut file = BufferedReader::new(File::open(&post_path));
    //let mut contents = "";
    let heading = content.next().unwrap();
    let mut content = content.map(|x| x.to_string());
    let content = content.fold("".to_string(), |a, b| a + "\n\n".to_string() + b);
    /*for line in file.lines() {
        let line = line.unwrap();
        let split: Vec<&str> = line.as_slice().rsplitn(1, ": ").collect();
        match split.get(1) {
            Ok(value) => {
                println!("value");
            }
            Err(e) => {
                contents.push_str(split.get(0));
            }
        }
        //print!("{}", line.unwrap());
        println!("{}", split);
    }*/
    let html_content = format!("{}", Markdown(content.as_slice()));

    let mut data = HashMap::<&str, &str>::new();
    data.insert("content", html_content.as_slice());
    response.render("assets/templates/post.tpl", &data);
}

pub fn custom_errors(err: &NickelError, _req: &Request, response: &mut Response) -> MiddlewareResult {
    match err.kind {
        ErrorWithStatusCode(Forbidden) => {
            handle_error(Forbidden, "403", "You are not authorized to view this page", response);
            Ok(Halt)
        },
        ErrorWithStatusCode(NotFound) => {
            handle_error(NotFound, "404", "That file could not be found.", response);
            Ok(Halt)
        },
        ErrorWithStatusCode(InternalServerError) => {
            handle_error(InternalServerError, "500", "An error has occured!", response);
            Ok(Halt)
        },
        _ => Ok(Continue)
    }
}

fn handle_error(status_code: Status, error_code: &str, error_message: &str, res: &mut Response) {
    let mut data = HashMap::<&str, &str>::new();

    data.insert("site_url", "http://nikitapek.in");
    data.insert("error_code", error_code);
    data.insert("error_message", error_message);

    res.content_type(MediaType::Html)
       .status_code(status_code)
       .render("assets/templates/error.tpl", &data);
}
<commit_msg>Implement Markdown post parsing through rust-peg<commit_after>use http::status::Status;
use http::status::Status::{Forbidden, NotFound, InternalServerError};
use nickel::{
    Continue,
    ErrorWithStatusCode,
    Halt,
    MiddlewareResult,
    NickelError,
    Request,
    Response,
};
use nickel::mimes::MediaType;
use rustdoc::html::markdown::Markdown;
use std::collections::HashMap;
use std::io::{
    BufferedReader,
    File
};

use parser;

/// Render the index page.
pub fn root_handler(_request: &Request, response: &mut Response) {
    let mut data = HashMap::<&str, &str>::new();
    data.insert("name", "user");
    response.render("assets/templates/index.tpl", &data);
}

pub fn get_blog_post(request: &Request, response: &mut Response) {
    // Retrieve the post id from the request URL.
    let post_year = request.param("post_year");
    let post_id = request.param("post_id");
    let post_path = Path::new("assets/content/posts/".to_string() +
            post_year.to_string() +
            "/".to_string() +
            post_id.to_string() +
            ".md".to_string());

    // Read the post markdown from the disk.
    let content = File::open(&post_path).read_to_end().unwrap();
    let content = String::from_utf8(content).unwrap();
    let post = match parser::post(content.as_slice()) {
        Ok(post) => post,
        Err(e) => {
            error!("Failed to parse post: {}", e);
            handle_error(InternalServerError, "500", "An internal error has occurred!", response);
            return
        }
    };
    let html_content = format!("{}", Markdown(post.content()));

    let mut data = HashMap::<&str, &str>::new();
    data.insert("content", html_content.as_slice());
    response.render("assets/templates/post.tpl", &data);
}

pub fn custom_errors(err: &NickelError, _req: &Request, response: &mut Response) -> MiddlewareResult {
    match err.kind {
        ErrorWithStatusCode(Forbidden) => {
            handle_error(Forbidden, "403", "You are not authorized to view this page", response);
            Ok(Halt)
        },
        ErrorWithStatusCode(NotFound) => {
            handle_error(NotFound, "404", "That file could not be found.", response);
            Ok(Halt)
        },
        ErrorWithStatusCode(InternalServerError) => {
            handle_error(InternalServerError, "500", "An error has occured!", response);
            Ok(Halt)
        },
        _ => Ok(Continue)
    }
}

fn handle_error(status_code: Status, error_code: &str, error_message: &str, res: &mut Response) {
    let mut data = HashMap::<&str, &str>::new();

    data.insert("site_url", "http://nikitapek.in");
    data.insert("error_code", error_code);
    data.insert("error_message", error_message);

    res.content_type(MediaType::Html)
       .status_code(status_code)
       .render("assets/templates/error.tpl", &data);
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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.

//! Commands that the GPU will execute (includes draw commands).
//!
//! With Vulkan, before the GPU can do anything you must create a `CommandBuffer`. A command buffer
//! is a list of commands that will executed by the GPU. Once a command buffer is created, you can
//! execute it. A command buffer must always be created even for the most simple tasks.
//!
//! # Primary and secondary command buffers.
//!
//! There are three types of command buffers:
//!
//! - **Primary command buffers**. They can contain any command. They are the only type of command
//!   buffer that can be submitted to a queue.
//! - **Secondary "graphics" command buffers**. They can only contain draw and clear commands.
//!   They can only be called from a primary command buffer when inside a render pass.
//! - **Secondary "compute" command buffers**. They can only contain non-render-pass-related
//!   commands (ie. everything but drawing, clearing, etc.) and cannot enter a render pass. They
//!   can only be called from a primary command buffer outside of a render pass.
//!
//! Using secondary command buffers leads to slightly lower performances on the GPU, but they have
//! two advantages on the CPU side:
//!
//! - Building a command buffer is a single-threaded operation, but by using secondary command
//!   buffers you can build multiple secondary command buffers in multiple threads simultaneously.
//! - Secondary command buffers can be kept alive between frames. When you always repeat the same
//!   operations, it might be a good idea to build a secondary command buffer once at
//!   initialization and then reuse it afterwards.
//!
//! # The `AutoCommandBufferBuilder`
//!
//! The most basic (and recommended) way to create a command buffer is to create a
//! [`AutoCommandBufferBuilder`](struct.AutoCommandBufferBuilder.html). Then use the
//! [`CommandBufferBuilder` trait](trait.CommandBufferBuilder.html) to add commands to it.
//! When you are done adding commands, use
//! [the `CommandBufferBuild` trait](trait.CommandBufferBuild.html) to obtain a
//! `AutoCommandBuffer`.
//!
//! Once built, use [the `CommandBuffer` trait](trait.CommandBuffer.html) to submit the command
//! buffer. Submitting a command buffer returns an object that implements the `GpuFuture` trait and
//! that represents the moment when the execution will end on the GPU.
//!
//! ```
//! use vulkano::command_buffer::AutoCommandBufferBuilder;
//! use vulkano::command_buffer::CommandBufferBuilder;
//! use vulkano::command_buffer::CommandBuffer;
//!
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
//! # let queue: std::sync::Arc<vulkano::device::Queue> = return;
//! let cb = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
//!     // TODO: add an actual command to this example
//!     .build().unwrap();
//!
//! let _future = cb.execute(queue.clone());
//! ```
//!
//! # Internal architecture of vulkano
//!
//! The `commands_raw` and `commands_extra` modules contain structs that correspond to various
//! commands that can be added to command buffer builders. A command can be added to a command
//! buffer builder by using the `AddCommand<C>` trait, where `C` is the command struct.
//!
//! The `AutoCommandBufferBuilder` internally uses a `UnsafeCommandBufferBuilder` wrapped around
//! multiple layers. See the `cb` module for more information.
//!
//! Command pools are automatically handled by default, but vulkano also allows you to use
//! alternative command pool implementations and use them. See the `pool` module for more
//! information.

pub use self::auto::AutoCommandBufferBuilder;
pub use self::auto::AutoCommandBuffer;
pub use self::state_cacher::StateCacher;
pub use self::state_cacher::StateCacherOutcome;
pub use self::traits::CommandBuffer;
pub use self::traits::CommandBufferBuild;
pub use self::traits::CommandBufferExecError;
pub use self::traits::CommandBufferExecFuture;

use pipeline::viewport::Viewport;
use pipeline::viewport::Scissor;

pub mod pool;
pub mod submit;
pub mod synced;
pub mod sys;
pub mod validity;

mod auto;
mod state_cacher;
mod traits;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DrawIndirectCommand {
    pub vertex_count: u32,
    pub instance_count: u32,
    pub first_vertex: u32,
    pub first_instance: u32,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DrawIndexedIndirectCommand {
    pub index_count: u32,
    pub instance_count: u32,
    pub first_index: u32,
    pub vertex_offset: u32,
    pub first_instance: u32,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DispatchIndirectCommand {
    pub x: u32,
    pub y: u32,
    pub z: u32,
}

/// The dynamic state to use for a draw command.
// TODO: probably not the right location
#[derive(Debug, Clone)]
pub struct DynamicState {
    pub line_width: Option<f32>,
    pub viewports: Option<Vec<Viewport>>,
    pub scissors: Option<Vec<Scissor>>,
    // TODO: missing fields
}

impl DynamicState {
    #[inline]
    pub fn none() -> DynamicState {
        DynamicState {
            line_width: None,
            viewports: None,
            scissors: None,
        }
    }
}

impl Default for DynamicState {
    #[inline]
    fn default() -> DynamicState {
        DynamicState::none()
    }
}
<commit_msg>Fix doctest<commit_after>// Copyright (c) 2016 The vulkano developers
// 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.

//! Commands that the GPU will execute (includes draw commands).
//!
//! With Vulkan, before the GPU can do anything you must create a `CommandBuffer`. A command buffer
//! is a list of commands that will executed by the GPU. Once a command buffer is created, you can
//! execute it. A command buffer must always be created even for the most simple tasks.
//!
//! # Primary and secondary command buffers.
//!
//! There are three types of command buffers:
//!
//! - **Primary command buffers**. They can contain any command. They are the only type of command
//!   buffer that can be submitted to a queue.
//! - **Secondary "graphics" command buffers**. They can only contain draw and clear commands.
//!   They can only be called from a primary command buffer when inside a render pass.
//! - **Secondary "compute" command buffers**. They can only contain non-render-pass-related
//!   commands (ie. everything but drawing, clearing, etc.) and cannot enter a render pass. They
//!   can only be called from a primary command buffer outside of a render pass.
//!
//! Using secondary command buffers leads to slightly lower performances on the GPU, but they have
//! two advantages on the CPU side:
//!
//! - Building a command buffer is a single-threaded operation, but by using secondary command
//!   buffers you can build multiple secondary command buffers in multiple threads simultaneously.
//! - Secondary command buffers can be kept alive between frames. When you always repeat the same
//!   operations, it might be a good idea to build a secondary command buffer once at
//!   initialization and then reuse it afterwards.
//!
//! # The `AutoCommandBufferBuilder`
//!
//! The most basic (and recommended) way to create a command buffer is to create a
//! [`AutoCommandBufferBuilder`](struct.AutoCommandBufferBuilder.html). Then use the
//! [`CommandBufferBuilder` trait](trait.CommandBufferBuilder.html) to add commands to it.
//! When you are done adding commands, use
//! [the `CommandBufferBuild` trait](trait.CommandBufferBuild.html) to obtain a
//! `AutoCommandBuffer`.
//!
//! Once built, use [the `CommandBuffer` trait](trait.CommandBuffer.html) to submit the command
//! buffer. Submitting a command buffer returns an object that implements the `GpuFuture` trait and
//! that represents the moment when the execution will end on the GPU.
//!
//! ```
//! use vulkano::command_buffer::AutoCommandBufferBuilder;
//! use vulkano::command_buffer::CommandBuffer;
//!
//! # let device: std::sync::Arc<vulkano::device::Device> = return;
//! # let queue: std::sync::Arc<vulkano::device::Queue> = return;
//! let cb = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
//!     // TODO: add an actual command to this example
//!     .build().unwrap();
//!
//! let _future = cb.execute(queue.clone());
//! ```
//!
//! # Internal architecture of vulkano
//!
//! The `commands_raw` and `commands_extra` modules contain structs that correspond to various
//! commands that can be added to command buffer builders. A command can be added to a command
//! buffer builder by using the `AddCommand<C>` trait, where `C` is the command struct.
//!
//! The `AutoCommandBufferBuilder` internally uses a `UnsafeCommandBufferBuilder` wrapped around
//! multiple layers. See the `cb` module for more information.
//!
//! Command pools are automatically handled by default, but vulkano also allows you to use
//! alternative command pool implementations and use them. See the `pool` module for more
//! information.

pub use self::auto::AutoCommandBufferBuilder;
pub use self::auto::AutoCommandBuffer;
pub use self::state_cacher::StateCacher;
pub use self::state_cacher::StateCacherOutcome;
pub use self::traits::CommandBuffer;
pub use self::traits::CommandBufferBuild;
pub use self::traits::CommandBufferExecError;
pub use self::traits::CommandBufferExecFuture;

use pipeline::viewport::Viewport;
use pipeline::viewport::Scissor;

pub mod pool;
pub mod submit;
pub mod synced;
pub mod sys;
pub mod validity;

mod auto;
mod state_cacher;
mod traits;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DrawIndirectCommand {
    pub vertex_count: u32,
    pub instance_count: u32,
    pub first_vertex: u32,
    pub first_instance: u32,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DrawIndexedIndirectCommand {
    pub index_count: u32,
    pub instance_count: u32,
    pub first_index: u32,
    pub vertex_offset: u32,
    pub first_instance: u32,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DispatchIndirectCommand {
    pub x: u32,
    pub y: u32,
    pub z: u32,
}

/// The dynamic state to use for a draw command.
// TODO: probably not the right location
#[derive(Debug, Clone)]
pub struct DynamicState {
    pub line_width: Option<f32>,
    pub viewports: Option<Vec<Viewport>>,
    pub scissors: Option<Vec<Scissor>>,
    // TODO: missing fields
}

impl DynamicState {
    #[inline]
    pub fn none() -> DynamicState {
        DynamicState {
            line_width: None,
            viewports: None,
            scissors: None,
        }
    }
}

impl Default for DynamicState {
    #[inline]
    fn default() -> DynamicState {
        DynamicState::none()
    }
}
<|endoftext|>