<commit_before>use std::path::PathBuf;
use std::collections::{HashMap, BTreeMap};
use std::{fmt, cmp};

use base32;
use url::Url;
use ring::digest;

use disk;
use bencode::BEncode;
use util::{hash_to_id, id_to_hash};

#[derive(Serialize, Deserialize, Clone)]
pub struct Info {
    pub name: String,
    pub announce: String,
    pub piece_len: u32,
    pub total_len: u64,
    pub hashes: Vec<Vec<u8>>,
    pub hash: [u8; 20],
    pub files: Vec<File>,
    pub file_idx: HashMap<PathBuf, usize>,
    pub private: bool,
    pub be_name: Option<Vec<u8>>,
}

impl fmt::Debug for Info {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Torrent Info {{
                name: {:?},
                announce: {:?},
                piece_len: {:?},
                total_len: {:?},
                hash: {:?},
                files: {:?}
            }}",
            self.name,
            self.announce,
            self.piece_len,
            self.total_len,
            hash_to_id(&self.hash),
            self.files
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct File {
    pub path: PathBuf,
    pub length: u64,
}

impl File {
    fn from_bencode(data: BEncode) -> Result<File, &'static str> {
        let mut d = data.into_dict().ok_or("File must be a dictionary type!")?;
        match (d.remove("name"), d.remove("path"), d.remove("length")) {
            (Some(v), None, Some(l)) => {
                let f = File {
                    path: PathBuf::from(v.into_string().ok_or("Path must be a valid string.")?),
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            (None, Some(path), Some(l)) => {
                let mut p = PathBuf::new();
                for dir in path.into_list().ok_or("File path should be a list")? {
                    p.push(
                        dir.into_string().ok_or("File path parts should be strings")?,
                    );
                }
                let f = File {
                    path: p,
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            _ => Err("File dict must contain length and name or path"),
        }
    }
}

impl Info {
    pub fn from_magnet(data: &str) -> Result<Info, &'static str> {
        let url = match Url::parse(data) {
            Ok(u) => u,
            Err(_) => return Err("Failed to parse magnet URL!"),
        };

        if url.scheme() != "magnet" {
            return Err("magnet URL must use magnet URL scheme");
        };
        let hash = url.query_pairs()
            .find(|&(ref k, ref v)| k == "xt" && v.starts_with("urn:btih:"))
            .and_then(|(_, ref v)| {
                id_to_hash(&v[9..]).or_else(|| {
                    base32::decode(base32::Alphabet::RFC4648 { padding: true }, &v[9..])
                        .and_then(|b| {
                            if b.len() != 20 {
                                return None;
                            }
                            let mut a = [0; 20];
                            (&mut a[..]).copy_from_slice(&b);
                            Some(a)
                        })
                })
            })
            .ok_or("No hash found in magnet")?;
        let announce = url.query_pairs()
            .find(|&(ref k, _)| k == "tr")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        let name = url.query_pairs()
            .find(|&(ref k, _)| k == "dn")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        Ok(Info {
            name,
            announce,
            piece_len: 0,
            total_len: 0,
            hashes: vec![],
            hash,
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        })
    }

    pub fn complete(&self) -> bool {
        !self.hashes.is_empty()
    }

    pub fn to_bencode(&self) -> BEncode {
        let mut info = BTreeMap::new();
        if let Some(ref n) = self.be_name {
            info.insert("name".to_owned(), BEncode::String(n.clone()));
        }
        if self.private {
            info.insert("private".to_owned(), BEncode::Int(1));
        }
        info.insert(
            "piece length".to_owned(),
            BEncode::Int(i64::from(self.piece_len)),
        );
        let mut pieces = Vec::with_capacity(self.hashes.len() * 20);
        for h in &self.hashes {
            pieces.extend_from_slice(h);
        }
        info.insert("pieces".to_owned(), BEncode::String(pieces));
        if self.files.len() == 1 {
            info.insert(
                "length".to_owned(),
                BEncode::Int(self.files[0].length as i64),
            );
        } else {
            let files = self.files
                .iter()
                .map(|f| {
                    let mut fb = BTreeMap::new();
                    fb.insert("length".to_owned(), BEncode::Int(f.length as i64));
                    fb.insert(
                        "path".to_owned(),
                        BEncode::String(
                            f.path
                                .clone()
                                .into_os_string()
                                .into_string()
                                .unwrap()
                                .into_bytes(),
                        ),
                    );
                    BEncode::Dict(fb)
                })
                .collect();
            info.insert("files".to_owned(), BEncode::List(files));
        }
        BEncode::Dict(info)
    }

    pub fn from_bencode(data: BEncode) -> Result<Info, &'static str> {
        data.into_dict()
            .and_then(|mut d| {
                d.remove("info").and_then(|i| i.into_dict()).map(|i| (d, i))
            })
            .ok_or("invalid info field")
            .and_then(|(mut d, mut i)| {
                let mut info_bytes = Vec::new();
                BEncode::Dict(i.clone()).encode(&mut info_bytes).unwrap();
                let mut ctx = digest::Context::new(&digest::SHA1);
                ctx.update(&info_bytes[..]);
                let digest = ctx.finish();
                let mut hash = [0u8; 20];
                hash.copy_from_slice(digest.as_ref());

                let a = d.remove("announce").and_then(|a| a.into_string()).ok_or(
                    "Info must have announce url",
                )?;
                let pl = i.remove("piece length").and_then(|i| i.into_int()).ok_or(
                    "Info must specify piece length",
                )?;
                let hashes = i.remove("pieces")
                    .and_then(|p| p.into_bytes())
                    .map(|mut p| {
                        let mut v = Vec::new();
                        while !p.is_empty() {
                            let remaining = p.split_off(20);
                            v.push(p);
                            p = remaining;
                        }
                        v
                    })
                    .ok_or("Info must provide valid hashes")?;

                let private = if let Some(v) = i.remove("private") {
                    v.into_int()
                        .and_then(|p| if p == 1 { Some(true) } else { None })
                        .ok_or("private key must be an integer equal to 1 if present!")?
                } else {
                    false
                };

                let be_name = if let Some(v) = i.get("name").cloned() {
                    Some(v.into_bytes().ok_or("name field must be a bitstring!")?)
                } else {
                    None
                };

                let files = parse_bencode_files(i)?;
                let name = if files.is_empty() {
                    files[0]
                        .path
                        .clone()
                        .into_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else if !files[0].path.has_root() {
                    let mut piter = files[0].path.components();
                    piter
                        .next()
                        .unwrap()
                        .as_os_str()
                        .to_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else {
                    unreachable!()
                };

                let mut file_idx = HashMap::new();
                for (i, file) in files.iter().enumerate() {
                    file_idx.insert(file.path.clone(), i);
                }
                let total_len = files.iter().map(|f| f.length).sum();
                Ok(Info {
                    name,
                    announce: a,
                    piece_len: pl as u32,
                    hashes,
                    hash,
                    files,
                    file_idx,
                    total_len,
                    private,
                    be_name,
                })
            })
    }

    #[cfg(test)]
    pub fn with_pieces(pieces: usize) -> Info {
        Info {
            name: String::from(""),
            announce: String::from(""),
            piece_len: 16_384,
            total_len: 16_384 * pieces as u64,
            hashes: vec![vec![0u8]; pieces],
            hash: [0u8; 20],
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        }
    }

    pub fn block_len(&self, idx: u32, offset: u32) -> u32 {
        if idx != self.pieces() - 1 {
            16_384
        } else {
            let last_piece_len = self.piece_len(idx);
            // Note this is not the real last block len, just what it will be IF the offset really
            // is for the last block
            let last_block_len = last_piece_len - offset;
            if offset < last_piece_len && last_block_len <= 16_384 {
                last_block_len
            } else {
                16_384
            }
        }
    }

    pub fn piece_len(&self, idx: u32) -> u32 {
        if !self.complete() {
            return 0;
        }
        if idx != self.pieces().saturating_sub(1) {
            self.piece_len
        } else {
            (self.total_len - u64::from(self.piece_len) * (u64::from(self.pieces()) - 1)) as u32
        }
    }

    pub fn pieces(&self) -> u32 {
        self.hashes.len() as u32
    }

    /// Calculates the file offsets for a given block at index/begin
    pub fn block_disk_locs(&self, index: u32, begin: u32) -> Vec<disk::Location> {
        let len = self.block_len(index, begin);
        self.calc_disk_locs(index, begin, len)
    }

    /// Calculates the file offsets for a given piece at index
    pub fn piece_disk_locs(&self, index: u32) -> Vec<disk::Location> {
        let len = self.piece_len(index);
        self.calc_disk_locs(index, 0, len)
    }

    /// Calculates the file offsets for a given index, begin, and block length.
    fn calc_disk_locs(&self, index: u32, begin: u32, len: u32) -> Vec<disk::Location> {
        let mut len = u64::from(len);
        // The absolute byte offset where we start processing data.
        let mut cur_start = u64::from(index) * u64::from(self.piece_len) + u64::from(begin);
        // Current index of the data block we're writing
        let mut data_start = 0;
        // The current file end length.
        let mut fidx = 0;
        // Iterate over all file lengths, if we find any which end a bigger
        // idx than cur_start, write from cur_start..cur_start + file_write_len for that file
        // and continue if we're now at the end of the file.
        let mut locs = Vec::new();
        for f in &self.files {
            fidx += f.length;
            if cur_start < fidx {
                let file_write_len = cmp::min(fidx - cur_start, len);
                let offset = cur_start - (fidx - f.length);
                if file_write_len == len {
                    // The file is longer than our len, just write to it,
                    // exit loop
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    break;
                } else {
                    // Write to the end of file, continue
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    len -= file_write_len;
                    cur_start += file_write_len;
                    data_start += file_write_len;
                }
            }
        }
        locs
    }
}

fn parse_bencode_files(mut data: BTreeMap<String, BEncode>) -> Result<Vec<File>, &'static str> {
    match data.remove("files").and_then(|l| l.into_list()) {
        Some(fs) => {
            let mut path = PathBuf::new();
            path.push(data.remove("name").and_then(|v| v.into_string()).ok_or(
                "Multifile mode must have a name field",
            )?);
            let mut files = Vec::new();
            for f in fs {
                let mut file = File::from_bencode(f)?;
                file.path = path.join(file.path);
                files.push(file);
            }
            Ok(files)
        }
        None => File::from_bencode(BEncode::Dict(data)).map(|f| vec![f]),
    }
}
<commit_msg>Add regression test for info piece size<commit_after>use std::path::PathBuf;
use std::collections::{HashMap, BTreeMap};
use std::{fmt, cmp};

use base32;
use url::Url;
use ring::digest;

use disk;
use bencode::BEncode;
use util::{hash_to_id, id_to_hash};

#[derive(Serialize, Deserialize, Clone)]
pub struct Info {
    pub name: String,
    pub announce: String,
    pub piece_len: u32,
    pub total_len: u64,
    pub hashes: Vec<Vec<u8>>,
    pub hash: [u8; 20],
    pub files: Vec<File>,
    pub file_idx: HashMap<PathBuf, usize>,
    pub private: bool,
    pub be_name: Option<Vec<u8>>,
}

impl fmt::Debug for Info {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Torrent Info {{
                name: {:?},
                announce: {:?},
                piece_len: {:?},
                total_len: {:?},
                hash: {:?},
                files: {:?}
            }}",
            self.name,
            self.announce,
            self.piece_len,
            self.total_len,
            hash_to_id(&self.hash),
            self.files
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct File {
    pub path: PathBuf,
    pub length: u64,
}

impl File {
    fn from_bencode(data: BEncode) -> Result<File, &'static str> {
        let mut d = data.into_dict().ok_or("File must be a dictionary type!")?;
        match (d.remove("name"), d.remove("path"), d.remove("length")) {
            (Some(v), None, Some(l)) => {
                let f = File {
                    path: PathBuf::from(v.into_string().ok_or("Path must be a valid string.")?),
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            (None, Some(path), Some(l)) => {
                let mut p = PathBuf::new();
                for dir in path.into_list().ok_or("File path should be a list")? {
                    p.push(
                        dir.into_string().ok_or("File path parts should be strings")?,
                    );
                }
                let f = File {
                    path: p,
                    length: l.into_int().ok_or("File length must be a valid int")? as u64,
                };
                Ok(f)
            }
            _ => Err("File dict must contain length and name or path"),
        }
    }
}

impl Info {
    pub fn from_magnet(data: &str) -> Result<Info, &'static str> {
        let url = match Url::parse(data) {
            Ok(u) => u,
            Err(_) => return Err("Failed to parse magnet URL!"),
        };

        if url.scheme() != "magnet" {
            return Err("magnet URL must use magnet URL scheme");
        };
        let hash = url.query_pairs()
            .find(|&(ref k, ref v)| k == "xt" && v.starts_with("urn:btih:"))
            .and_then(|(_, ref v)| {
                id_to_hash(&v[9..]).or_else(|| {
                    base32::decode(base32::Alphabet::RFC4648 { padding: true }, &v[9..])
                        .and_then(|b| {
                            if b.len() != 20 {
                                return None;
                            }
                            let mut a = [0; 20];
                            (&mut a[..]).copy_from_slice(&b);
                            Some(a)
                        })
                })
            })
            .ok_or("No hash found in magnet")?;
        let announce = url.query_pairs()
            .find(|&(ref k, _)| k == "tr")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        let name = url.query_pairs()
            .find(|&(ref k, _)| k == "dn")
            .map(|(_, ref v)| v.to_string())
            .unwrap_or_else(|| "".to_owned());
        Ok(Info {
            name,
            announce,
            piece_len: 0,
            total_len: 0,
            hashes: vec![],
            hash,
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        })
    }

    pub fn complete(&self) -> bool {
        !self.hashes.is_empty()
    }

    pub fn to_bencode(&self) -> BEncode {
        let mut info = BTreeMap::new();
        if let Some(ref n) = self.be_name {
            info.insert("name".to_owned(), BEncode::String(n.clone()));
        }
        if self.private {
            info.insert("private".to_owned(), BEncode::Int(1));
        }
        info.insert(
            "piece length".to_owned(),
            BEncode::Int(i64::from(self.piece_len)),
        );
        let mut pieces = Vec::with_capacity(self.hashes.len() * 20);
        for h in &self.hashes {
            pieces.extend_from_slice(h);
        }
        info.insert("pieces".to_owned(), BEncode::String(pieces));
        if self.files.len() == 1 {
            info.insert(
                "length".to_owned(),
                BEncode::Int(self.files[0].length as i64),
            );
        } else {
            let files = self.files
                .iter()
                .map(|f| {
                    let mut fb = BTreeMap::new();
                    fb.insert("length".to_owned(), BEncode::Int(f.length as i64));
                    fb.insert(
                        "path".to_owned(),
                        BEncode::String(
                            f.path
                                .clone()
                                .into_os_string()
                                .into_string()
                                .unwrap()
                                .into_bytes(),
                        ),
                    );
                    BEncode::Dict(fb)
                })
                .collect();
            info.insert("files".to_owned(), BEncode::List(files));
        }
        BEncode::Dict(info)
    }

    pub fn from_bencode(data: BEncode) -> Result<Info, &'static str> {
        data.into_dict()
            .and_then(|mut d| {
                d.remove("info").and_then(|i| i.into_dict()).map(|i| (d, i))
            })
            .ok_or("invalid info field")
            .and_then(|(mut d, mut i)| {
                let mut info_bytes = Vec::new();
                BEncode::Dict(i.clone()).encode(&mut info_bytes).unwrap();
                let mut ctx = digest::Context::new(&digest::SHA1);
                ctx.update(&info_bytes[..]);
                let digest = ctx.finish();
                let mut hash = [0u8; 20];
                hash.copy_from_slice(digest.as_ref());

                let a = d.remove("announce").and_then(|a| a.into_string()).ok_or(
                    "Info must have announce url",
                )?;
                let pl = i.remove("piece length").and_then(|i| i.into_int()).ok_or(
                    "Info must specify piece length",
                )?;
                let hashes = i.remove("pieces")
                    .and_then(|p| p.into_bytes())
                    .map(|mut p| {
                        let mut v = Vec::new();
                        while !p.is_empty() {
                            let remaining = p.split_off(20);
                            v.push(p);
                            p = remaining;
                        }
                        v
                    })
                    .ok_or("Info must provide valid hashes")?;

                let private = if let Some(v) = i.remove("private") {
                    v.into_int()
                        .and_then(|p| if p == 1 { Some(true) } else { None })
                        .ok_or("private key must be an integer equal to 1 if present!")?
                } else {
                    false
                };

                let be_name = if let Some(v) = i.get("name").cloned() {
                    Some(v.into_bytes().ok_or("name field must be a bitstring!")?)
                } else {
                    None
                };

                let files = parse_bencode_files(i)?;
                let name = if files.is_empty() {
                    files[0]
                        .path
                        .clone()
                        .into_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else if !files[0].path.has_root() {
                    let mut piter = files[0].path.components();
                    piter
                        .next()
                        .unwrap()
                        .as_os_str()
                        .to_os_string()
                        .into_string()
                        .map_err(|_| "Only UTF8 paths are accepted")?
                } else {
                    unreachable!()
                };

                let mut file_idx = HashMap::new();
                for (i, file) in files.iter().enumerate() {
                    file_idx.insert(file.path.clone(), i);
                }
                let total_len = files.iter().map(|f| f.length).sum();
                Ok(Info {
                    name,
                    announce: a,
                    piece_len: pl as u32,
                    hashes,
                    hash,
                    files,
                    file_idx,
                    total_len,
                    private,
                    be_name,
                })
            })
    }

    #[cfg(test)]
    pub fn with_pieces(pieces: usize) -> Info {
        Info {
            name: String::from(""),
            announce: String::from(""),
            piece_len: 16_384,
            total_len: 16_384 * pieces as u64,
            hashes: vec![vec![0u8]; pieces],
            hash: [0u8; 20],
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        }
    }

    #[cfg(test)]
    pub fn with_pieces_scale(pieces: u32, scale: u32) -> Info {
        Info {
            name: String::from(""),
            announce: String::from(""),
            piece_len: 16_384 * scale,
            total_len: 16_384 * pieces as u64 * scale as u64,
            hashes: vec![vec![0u8]; pieces as usize],
            hash: [0u8; 20],
            files: vec![],
            file_idx: HashMap::new(),
            private: false,
            be_name: None,
        }
    }

    pub fn block_len(&self, idx: u32, offset: u32) -> u32 {
        if idx != self.pieces() - 1 {
            16_384
        } else {
            let last_piece_len = self.piece_len(idx);
            // Note this is not the real last block len, just what it will be IF the offset really
            // is for the last block
            let last_block_len = last_piece_len - offset;
            if offset < last_piece_len && last_block_len <= 16_384 {
                last_block_len
            } else {
                16_384
            }
        }
    }

    pub fn piece_len(&self, idx: u32) -> u32 {
        if !self.complete() {
            return 0;
        }
        if idx != self.pieces().saturating_sub(1) {
            self.piece_len
        } else {
            (self.total_len - u64::from(self.piece_len) * (u64::from(self.pieces()) - 1)) as u32
        }
    }

    pub fn pieces(&self) -> u32 {
        self.hashes.len() as u32
    }

    /// Calculates the file offsets for a given block at index/begin
    pub fn block_disk_locs(&self, index: u32, begin: u32) -> Vec<disk::Location> {
        let len = self.block_len(index, begin);
        self.calc_disk_locs(index, begin, len)
    }

    /// Calculates the file offsets for a given piece at index
    pub fn piece_disk_locs(&self, index: u32) -> Vec<disk::Location> {
        let len = self.piece_len(index);
        self.calc_disk_locs(index, 0, len)
    }

    /// Calculates the file offsets for a given index, begin, and block length.
    fn calc_disk_locs(&self, index: u32, begin: u32, len: u32) -> Vec<disk::Location> {
        let mut len = u64::from(len);
        // The absolute byte offset where we start processing data.
        let mut cur_start = u64::from(index) * u64::from(self.piece_len) + u64::from(begin);
        // Current index of the data block we're writing
        let mut data_start = 0;
        // The current file end length.
        let mut fidx = 0;
        // Iterate over all file lengths, if we find any which end a bigger
        // idx than cur_start, write from cur_start..cur_start + file_write_len for that file
        // and continue if we're now at the end of the file.
        let mut locs = Vec::new();
        for f in &self.files {
            fidx += f.length;
            if cur_start < fidx {
                let file_write_len = cmp::min(fidx - cur_start, len);
                let offset = cur_start - (fidx - f.length);
                if file_write_len == len {
                    // The file is longer than our len, just write to it,
                    // exit loop
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    break;
                } else {
                    // Write to the end of file, continue
                    locs.push(disk::Location::new(
                        f.path.clone(),
                        offset,
                        data_start,
                        data_start + file_write_len,
                    ));
                    len -= file_write_len;
                    cur_start += file_write_len;
                    data_start += file_write_len;
                }
            }
        }
        locs
    }
}

fn parse_bencode_files(mut data: BTreeMap<String, BEncode>) -> Result<Vec<File>, &'static str> {
    match data.remove("files").and_then(|l| l.into_list()) {
        Some(fs) => {
            let mut path = PathBuf::new();
            path.push(data.remove("name").and_then(|v| v.into_string()).ok_or(
                "Multifile mode must have a name field",
            )?);
            let mut files = Vec::new();
            for f in fs {
                let mut file = File::from_bencode(f)?;
                file.path = path.join(file.path);
                files.push(file);
            }
            Ok(files)
        }
        None => File::from_bencode(BEncode::Dict(data)).map(|f| vec![f]),
    }
}

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

    #[test]
    fn correct_piece_len() {
        let scale = 3;
        let pieces = 15;
        let mut info = Info::with_pieces_scale(pieces, scale);
        let end = 16_700u32;
        info.total_len += end as u64;
        info.hashes.push(vec![]);
        for i in 0..pieces {
            assert_eq!(info.piece_len(i), info.piece_len);
            for o in 0..scale {
                assert_eq!(info.block_len(i, o * 16_384), 16_384);
            }
        }
        assert_eq!(info.piece_len(pieces), end as u32);
        assert_eq!(info.block_len(pieces, 0), 16_384);
        assert_eq!(info.block_len(pieces, 16_384), (end % 16_384) as u32);
    }
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use libc::{self, c_char};
use std::slice;
use std::cell::RefCell;
use vec_map::VecMap;
use std::ffi::CString;

use super::PluginDescriptor;
use super::get_ladspa_descriptor;

// essentially ladspa.h API translated to rust.
pub mod ladspa {
    use libc::{c_void, c_char};

    pub type Data = f32;
    pub type Properties = i32;
    pub type PortDescriptor = i32;
    pub type PortRangeHintDescriptor = i32;

    pub type Handle = *mut c_void;

    #[repr(C)]
    #[derive(Copy, Clone)]
    pub struct PortRangeHint {
        pub hint_descriptor: PortRangeHintDescriptor,
        pub lower_bound: Data,
        pub upper_bound: Data,
    }

    #[repr(C)]
    #[allow(missing_copy_implementations)] // Remove this for a fun warning/suggestion cycle!
    pub struct Descriptor {
        pub unique_id: u64,
        pub label: *const c_char,
        pub properties: Properties,
        pub name: *const c_char,
        pub maker: *const c_char,
        pub copyright: *const c_char,
        pub port_count: u64,
        pub port_descriptors: *mut PortDescriptor,
        pub port_names: *mut *const c_char,
        pub port_range_hints: *mut PortRangeHint,
        pub implementation_data: *mut c_void,
        pub instantiate: extern "C" fn(descriptor: *const Descriptor, sample_rate: u64) -> Handle,
        pub connect_port: extern "C" fn(instance: Handle, port: u64, data_location: *mut Data),
        pub activate: Option<extern "C" fn(instance: Handle)>,
        pub run: extern "C" fn(instance: Handle, sample_count: u64),
        pub run_adding: Option<extern "C" fn(instance: Handle, sample_count: u64)>,
        pub set_run_adding_gain: Option<extern "C" fn(instance: Handle, gain: Data)>,
        pub deactivate: Option<extern "C" fn(instance: Handle)>,
        pub cleanup: extern "C" fn(instance: Handle),
    }

    pub const PROPERTY_REALTIME: Properties = 0x1;
    pub const PROPERTY_INPLACE_BROKEN: Properties = 0x2;
    pub const PROPERTY_HARD_RT_CAPABLE: Properties = 0x4;

    pub const PORT_INPUT: PortDescriptor = 0x1;
    pub const PORT_OUTPUT: PortDescriptor = 0x2;
    pub const PORT_CONTROL: PortDescriptor = 0x4;
    pub const PORT_AUDIO: PortDescriptor = 0x8;

    pub const HINT_BOUNDED_BELOW: PortRangeHintDescriptor = 0x1;
    pub const HINT_BOUNDED_ABOVE: PortRangeHintDescriptor = 0x2;
    pub const HINT_TOGGLED: PortRangeHintDescriptor = 0x4;
    pub const HINT_SAMPLE_RATE: PortRangeHintDescriptor = 0x8;
    pub const HINT_LOGARITHMIC: PortRangeHintDescriptor = 0x10;
    pub const HINT_INTEGER: PortRangeHintDescriptor = 0x20;
    pub const HINT_DEFAULT_MINIMUM: PortRangeHintDescriptor = 0x40;
    pub const HINT_DEFAULT_LOW: PortRangeHintDescriptor = 0x80;
    pub const HINT_DEFAULT_MIDDLE: PortRangeHintDescriptor = 0xC0;
    pub const HINT_DEFAULT_HIGH: PortRangeHintDescriptor = 0x100;
    pub const HINT_DEFAULT_MAXIMUM: PortRangeHintDescriptor = 0x140;
    pub const HINT_DEFAULT_0: PortRangeHintDescriptor = 0x200;
    pub const HINT_DEFAULT_1: PortRangeHintDescriptor = 0x240;
    pub const HINT_DEFAULT_100: PortRangeHintDescriptor = 0x280;
    pub const HINT_DEFAULT_440: PortRangeHintDescriptor = 0x2C0;
}

static mut descriptors: *mut Vec<*mut ladspa::Descriptor> = 0 as *mut Vec<*mut ladspa::Descriptor>;

// It seems that ladspa_descriptor is deleted during link time optimization unless we
// call it from somewhere.
#[allow(dead_code)]
unsafe fn _lto_workaround() {
    ladspa_descriptor(0);
}

#[no_mangle]
// Exported so the plugin is recognised by ladspa hosts.
pub unsafe extern "C" fn ladspa_descriptor(index: u64) -> *mut ladspa::Descriptor {
    if descriptors == ptr::null_mut() {
        libc::atexit(global_destruct);
        descriptors = mem::transmute(Box::new(Vec::<*mut ladspa::Descriptor>::new()));
    }

    // If it's already been generated, return the cached copy.
    if (index as usize) < (*descriptors).len() {
        return mem::transmute(&*(*descriptors)[index as usize])
    }

    match get_ladspa_descriptor(index) {
        Some(plugin) => {
            let desc = mem::transmute(Box::new(ladspa::Descriptor {
                unique_id: plugin.unique_id,
                label: CString::new(plugin.label).unwrap().into_ptr(),
                properties: plugin.properties.bits(),
                name: CString::new(plugin.name).unwrap().into_ptr(),
                maker: CString::new(plugin.maker).unwrap().into_ptr(),
                copyright: CString::new(plugin.copyright).unwrap().into_ptr(),

                port_count: plugin.ports.len() as u64,
                port_descriptors: mem::transmute::<_, &mut [i32]>(
                    plugin.ports.iter().map(|port|
                        port.desc as i32
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),
                port_names: mem::transmute::<_, &mut [*const c_char]>(
                    plugin.ports.iter().map(|port|
                        CString::new(port.name).unwrap().into_ptr()
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),
                port_range_hints: mem::transmute::<_, &mut [ladspa::PortRangeHint]>(
                    plugin.ports.iter().map(|port|
                        ladspa::PortRangeHint {
                            hint_descriptor: port.hint.map(|x| x.bits()).unwrap_or(0) |
                                port.default.map(|x| x as i32).unwrap_or(0) |
                                port.lower_bound.map(|_| ladspa::HINT_BOUNDED_BELOW).unwrap_or(0) |
                                port.upper_bound.map(|_| ladspa::HINT_BOUNDED_ABOVE).unwrap_or(0),
                            lower_bound: port.lower_bound.unwrap_or(0_f32),
                            upper_bound: port.upper_bound.unwrap_or(0_f32),
                        }
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),

                implementation_data: mem::transmute(Box::new(plugin)),

                instantiate: instantiate,
                connect_port: connect_port,
                run: run,
                cleanup: cleanup,
                run_adding: None,
                set_run_adding_gain: None,
                activate: Some(activate),
                deactivate: Some(deactivate),
            }));

            // store in global descriptor table
            (*descriptors).push(desc);
            desc
        }
        None => ptr::null_mut()
    }
}

extern "C" fn global_destruct() {
    unsafe {
        let descs: Box<Vec<*mut ladspa::Descriptor>> = mem::transmute(descriptors);
        for desc in descs.iter() {
            drop_descriptor(mem::transmute(*desc));
        }
    }
}

unsafe fn drop_descriptor(desc: &mut ladspa::Descriptor) {
    CString::from_ptr(desc.label);
    CString::from_ptr(desc.name);
    CString::from_ptr(desc.maker);
    CString::from_ptr(desc.copyright);
    Vec::from_raw_parts(desc.port_descriptors, desc.port_count as usize, desc.port_count as usize);
    Vec::from_raw_parts(desc.port_names, desc.port_count as usize, desc.port_count as usize)
        .iter().map(|&x| CString::from_ptr(x)).collect::<Vec<_>>();
    Vec::from_raw_parts(desc.port_range_hints, desc.port_count as usize, desc.port_count as usize);
    mem::transmute::<_, Box<PluginDescriptor>>(desc.implementation_data);
}

// The handle that is given to ladspa.
struct Handle<'a> {
    descriptor: &'static super::PluginDescriptor,
    plugin: Box<super::Plugin + 'static>,
    port_map: VecMap<super::PortConnection<'a>>,
    ports: Vec<&'a super::PortConnection<'a>>,
}

extern "C" fn instantiate(descriptor: *const ladspa::Descriptor, sample_rate: u64) -> ladspa::Handle {
    unsafe {
        let desc: &mut ladspa::Descriptor = mem::transmute(descriptor);

        let rust_desc: &super::PluginDescriptor = mem::transmute(desc.implementation_data);
        let rust_plugin = (rust_desc.new)(rust_desc, sample_rate);
        let port_map: VecMap<super::PortConnection> = VecMap::new();
        let ports: Vec<&super::PortConnection> = Vec::new();

        mem::transmute(Box::new(Handle {
            descriptor: rust_desc,
            plugin: rust_plugin,
            port_map: port_map,
            ports: ports,
        }))
    }
}

extern "C" fn connect_port(instance: ladspa::Handle, port_num: u64, data_location: *mut ladspa::Data) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);

        let port = handle.descriptor.ports[port_num as usize];

        // Create appropriate pointers to port data. Mutable locations are wrapped in refcells.
        let data = match port.desc {
            super::PortDescriptor::AudioInput => {
                super::PortData::AudioInput( // Initially create a size 0 slice because we don't know how big
                    slice::from_raw_parts(data_location, 0)) // it will be yet.
            },
            super::PortDescriptor::AudioOutput => {
                super::PortData::AudioOutput(RefCell::new( // Same here.
                    slice::from_raw_parts_mut(data_location, 0)))
            },
            super::PortDescriptor::ControlInput => {
                super::PortData::ControlInput(mem::transmute(data_location))
            },
            super::PortDescriptor::ControlOutput => {
                super::PortData::ControlOutput(RefCell::new(mem::transmute(data_location)))
            },
            super::PortDescriptor::Invalid => panic!("Invalid port descriptor!"),
        };

        let conn = super::PortConnection {
            port: port,
            data: data,
        };
        handle.port_map.insert(port_num as usize, conn);

        // Depends on the assumption that ports will be recreated whenever port_map changes
        let handle_ptr: &mut Handle = mem::transmute(instance);
        if handle.port_map.len() == handle.descriptor.ports.len() {
            handle_ptr.ports = handle.port_map.values().collect();
        }
    }
}

extern "C" fn run(instance: ladspa::Handle, sample_count: u64) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        for (_, port) in handle.port_map.iter_mut() {
            match port.data {
                super::PortData::AudioOutput(ref mut data) => {
                    let ptr = data.borrow_mut().as_mut_ptr();
                    *data.borrow_mut() = slice::from_raw_parts_mut(ptr, sample_count as usize);
                },
                super::PortData::AudioInput(ref mut data) => {
                    let ptr = data.as_ptr();
                    *data = slice::from_raw_parts(ptr, sample_count as usize);
                },
                _ => { }
            }
        }
        handle.plugin.run(sample_count as usize, &handle.ports);
    }
}

extern "C" fn activate(instance: ladspa::Handle) {
    unsafe{
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.activate();
    }
}
extern "C" fn deactivate(instance: ladspa::Handle) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.deactivate();
    }
}

/*
extern "C" fn run_adding(instance: ladspa::Handle, sample_count: u64) {
}
extern "C" fn set_run_adding_gain(instance: ladspa::Handle, gain: ladspa::Data) {
}
*/

extern "C" fn cleanup(instance: ladspa::Handle) {
    unsafe {
        mem::transmute::<_, Box<Handle>>(instance);
    }
}

<commit_msg>Fix for renames on CString<commit_after>use std::{mem, ptr};
use libc::{self, c_char};
use std::slice;
use std::cell::RefCell;
use vec_map::VecMap;
use std::ffi::CString;

use super::PluginDescriptor;
use super::get_ladspa_descriptor;

// essentially ladspa.h API translated to rust.
pub mod ladspa {
    use libc::{c_void, c_char};

    pub type Data = f32;
    pub type Properties = i32;
    pub type PortDescriptor = i32;
    pub type PortRangeHintDescriptor = i32;

    pub type Handle = *mut c_void;

    #[repr(C)]
    #[derive(Copy, Clone)]
    pub struct PortRangeHint {
        pub hint_descriptor: PortRangeHintDescriptor,
        pub lower_bound: Data,
        pub upper_bound: Data,
    }

    #[repr(C)]
    #[allow(missing_copy_implementations)] // Remove this for a fun warning/suggestion cycle!
    pub struct Descriptor {
        pub unique_id: u64,
        pub label: *const c_char,
        pub properties: Properties,
        pub name: *const c_char,
        pub maker: *const c_char,
        pub copyright: *const c_char,
        pub port_count: u64,
        pub port_descriptors: *mut PortDescriptor,
        pub port_names: *mut *const c_char,
        pub port_range_hints: *mut PortRangeHint,
        pub implementation_data: *mut c_void,
        pub instantiate: extern "C" fn(descriptor: *const Descriptor, sample_rate: u64) -> Handle,
        pub connect_port: extern "C" fn(instance: Handle, port: u64, data_location: *mut Data),
        pub activate: Option<extern "C" fn(instance: Handle)>,
        pub run: extern "C" fn(instance: Handle, sample_count: u64),
        pub run_adding: Option<extern "C" fn(instance: Handle, sample_count: u64)>,
        pub set_run_adding_gain: Option<extern "C" fn(instance: Handle, gain: Data)>,
        pub deactivate: Option<extern "C" fn(instance: Handle)>,
        pub cleanup: extern "C" fn(instance: Handle),
    }

    pub const PROPERTY_REALTIME: Properties = 0x1;
    pub const PROPERTY_INPLACE_BROKEN: Properties = 0x2;
    pub const PROPERTY_HARD_RT_CAPABLE: Properties = 0x4;

    pub const PORT_INPUT: PortDescriptor = 0x1;
    pub const PORT_OUTPUT: PortDescriptor = 0x2;
    pub const PORT_CONTROL: PortDescriptor = 0x4;
    pub const PORT_AUDIO: PortDescriptor = 0x8;

    pub const HINT_BOUNDED_BELOW: PortRangeHintDescriptor = 0x1;
    pub const HINT_BOUNDED_ABOVE: PortRangeHintDescriptor = 0x2;
    pub const HINT_TOGGLED: PortRangeHintDescriptor = 0x4;
    pub const HINT_SAMPLE_RATE: PortRangeHintDescriptor = 0x8;
    pub const HINT_LOGARITHMIC: PortRangeHintDescriptor = 0x10;
    pub const HINT_INTEGER: PortRangeHintDescriptor = 0x20;
    pub const HINT_DEFAULT_MINIMUM: PortRangeHintDescriptor = 0x40;
    pub const HINT_DEFAULT_LOW: PortRangeHintDescriptor = 0x80;
    pub const HINT_DEFAULT_MIDDLE: PortRangeHintDescriptor = 0xC0;
    pub const HINT_DEFAULT_HIGH: PortRangeHintDescriptor = 0x100;
    pub const HINT_DEFAULT_MAXIMUM: PortRangeHintDescriptor = 0x140;
    pub const HINT_DEFAULT_0: PortRangeHintDescriptor = 0x200;
    pub const HINT_DEFAULT_1: PortRangeHintDescriptor = 0x240;
    pub const HINT_DEFAULT_100: PortRangeHintDescriptor = 0x280;
    pub const HINT_DEFAULT_440: PortRangeHintDescriptor = 0x2C0;
}

static mut descriptors: *mut Vec<*mut ladspa::Descriptor> = 0 as *mut Vec<*mut ladspa::Descriptor>;

// It seems that ladspa_descriptor is deleted during link time optimization unless we
// call it from somewhere.
#[allow(dead_code)]
unsafe fn _lto_workaround() {
    ladspa_descriptor(0);
}

#[no_mangle]
// Exported so the plugin is recognised by ladspa hosts.
pub unsafe extern "C" fn ladspa_descriptor(index: u64) -> *mut ladspa::Descriptor {
    if descriptors == ptr::null_mut() {
        libc::atexit(global_destruct);
        descriptors = mem::transmute(Box::new(Vec::<*mut ladspa::Descriptor>::new()));
    }

    // If it's already been generated, return the cached copy.
    if (index as usize) < (*descriptors).len() {
        return mem::transmute(&*(*descriptors)[index as usize])
    }

    match get_ladspa_descriptor(index) {
        Some(plugin) => {
            let desc = mem::transmute(Box::new(ladspa::Descriptor {
                unique_id: plugin.unique_id,
                label: CString::new(plugin.label).unwrap().into_raw(),
                properties: plugin.properties.bits(),
                name: CString::new(plugin.name).unwrap().into_raw(),
                maker: CString::new(plugin.maker).unwrap().into_raw(),
                copyright: CString::new(plugin.copyright).unwrap().into_raw(),

                port_count: plugin.ports.len() as u64,
                port_descriptors: mem::transmute::<_, &mut [i32]>(
                    plugin.ports.iter().map(|port|
                        port.desc as i32
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),
                port_names: mem::transmute::<_, &mut [*const c_char]>(
                    plugin.ports.iter().map(|port|
                        CString::new(port.name).unwrap().into_raw()
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),
                port_range_hints: mem::transmute::<_, &mut [ladspa::PortRangeHint]>(
                    plugin.ports.iter().map(|port|
                        ladspa::PortRangeHint {
                            hint_descriptor: port.hint.map(|x| x.bits()).unwrap_or(0) |
                                port.default.map(|x| x as i32).unwrap_or(0) |
                                port.lower_bound.map(|_| ladspa::HINT_BOUNDED_BELOW).unwrap_or(0) |
                                port.upper_bound.map(|_| ladspa::HINT_BOUNDED_ABOVE).unwrap_or(0),
                            lower_bound: port.lower_bound.unwrap_or(0_f32),
                            upper_bound: port.upper_bound.unwrap_or(0_f32),
                        }
                    ).collect::<Vec<_>>().into_boxed_slice()).as_mut_ptr(),

                implementation_data: mem::transmute(Box::new(plugin)),

                instantiate: instantiate,
                connect_port: connect_port,
                run: run,
                cleanup: cleanup,
                run_adding: None,
                set_run_adding_gain: None,
                activate: Some(activate),
                deactivate: Some(deactivate),
            }));

            // store in global descriptor table
            (*descriptors).push(desc);
            desc
        }
        None => ptr::null_mut()
    }
}

extern "C" fn global_destruct() {
    unsafe {
        let descs: Box<Vec<*mut ladspa::Descriptor>> = mem::transmute(descriptors);
        for desc in descs.iter() {
            drop_descriptor(mem::transmute(*desc));
        }
    }
}

unsafe fn drop_descriptor(desc: &mut ladspa::Descriptor) {
    CString::from_raw(desc.label);
    CString::from_raw(desc.name);
    CString::from_raw(desc.maker);
    CString::from_raw(desc.copyright);
    Vec::from_raw_parts(desc.port_descriptors, desc.port_count as usize, desc.port_count as usize);
    Vec::from_raw_parts(desc.port_names, desc.port_count as usize, desc.port_count as usize)
        .iter().map(|&x| CString::from_raw(x)).collect::<Vec<_>>();
    Vec::from_raw_parts(desc.port_range_hints, desc.port_count as usize, desc.port_count as usize);
    mem::transmute::<_, Box<PluginDescriptor>>(desc.implementation_data);
}

// The handle that is given to ladspa.
struct Handle<'a> {
    descriptor: &'static super::PluginDescriptor,
    plugin: Box<super::Plugin + 'static>,
    port_map: VecMap<super::PortConnection<'a>>,
    ports: Vec<&'a super::PortConnection<'a>>,
}

extern "C" fn instantiate(descriptor: *const ladspa::Descriptor, sample_rate: u64) -> ladspa::Handle {
    unsafe {
        let desc: &mut ladspa::Descriptor = mem::transmute(descriptor);

        let rust_desc: &super::PluginDescriptor = mem::transmute(desc.implementation_data);
        let rust_plugin = (rust_desc.new)(rust_desc, sample_rate);
        let port_map: VecMap<super::PortConnection> = VecMap::new();
        let ports: Vec<&super::PortConnection> = Vec::new();

        mem::transmute(Box::new(Handle {
            descriptor: rust_desc,
            plugin: rust_plugin,
            port_map: port_map,
            ports: ports,
        }))
    }
}

extern "C" fn connect_port(instance: ladspa::Handle, port_num: u64, data_location: *mut ladspa::Data) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);

        let port = handle.descriptor.ports[port_num as usize];

        // Create appropriate pointers to port data. Mutable locations are wrapped in refcells.
        let data = match port.desc {
            super::PortDescriptor::AudioInput => {
                super::PortData::AudioInput( // Initially create a size 0 slice because we don't know how big
                    slice::from_raw_parts(data_location, 0)) // it will be yet.
            },
            super::PortDescriptor::AudioOutput => {
                super::PortData::AudioOutput(RefCell::new( // Same here.
                    slice::from_raw_parts_mut(data_location, 0)))
            },
            super::PortDescriptor::ControlInput => {
                super::PortData::ControlInput(mem::transmute(data_location))
            },
            super::PortDescriptor::ControlOutput => {
                super::PortData::ControlOutput(RefCell::new(mem::transmute(data_location)))
            },
            super::PortDescriptor::Invalid => panic!("Invalid port descriptor!"),
        };

        let conn = super::PortConnection {
            port: port,
            data: data,
        };
        handle.port_map.insert(port_num as usize, conn);

        // Depends on the assumption that ports will be recreated whenever port_map changes
        let handle_ptr: &mut Handle = mem::transmute(instance);
        if handle.port_map.len() == handle.descriptor.ports.len() {
            handle_ptr.ports = handle.port_map.values().collect();
        }
    }
}

extern "C" fn run(instance: ladspa::Handle, sample_count: u64) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        for (_, port) in handle.port_map.iter_mut() {
            match port.data {
                super::PortData::AudioOutput(ref mut data) => {
                    let ptr = data.borrow_mut().as_mut_ptr();
                    *data.borrow_mut() = slice::from_raw_parts_mut(ptr, sample_count as usize);
                },
                super::PortData::AudioInput(ref mut data) => {
                    let ptr = data.as_ptr();
                    *data = slice::from_raw_parts(ptr, sample_count as usize);
                },
                _ => { }
            }
        }
        handle.plugin.run(sample_count as usize, &handle.ports);
    }
}

extern "C" fn activate(instance: ladspa::Handle) {
    unsafe{
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.activate();
    }
}
extern "C" fn deactivate(instance: ladspa::Handle) {
    unsafe {
        let handle: &mut Handle = mem::transmute(instance);
        handle.plugin.deactivate();
    }
}

/*
extern "C" fn run_adding(instance: ladspa::Handle, sample_count: u64) {
}
extern "C" fn set_run_adding_gain(instance: ladspa::Handle, gain: ladspa::Data) {
}
*/

extern "C" fn cleanup(instance: ladspa::Handle) {
    unsafe {
        mem::transmute::<_, Box<Handle>>(instance);
    }
}

<|endoftext|>


<commit_before>use std::str;
use std::io::{self, Read, Write};
use std::fs::File;
use std::path::Path;
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::sync::Arc;
use std::clone::Clone;

use hyper;
use hyper::{Get, Post, StatusCode};
use hyper::header::{ContentLength, ContentType};

use hyper::server::{Http, Service};
use hyper::server::Request as HyperRequest;
use hyper::server::Response as HyperResponse;
use hyper::Method;
use hyper::HttpVersion;

use futures;
use futures::future::FutureResult;
use mime_types::Types as MimeTypes;

pub use typemap::Key;
pub use hyper::header::Headers;
pub use hyper::header;
pub use hyper::mime;
pub use request::SapperRequest;
pub use response::SapperResponse;
pub use router_m::Router;
pub use router::SapperRouter;
pub use handler::SapperHandler;

#[derive(Clone)]
pub struct PathParams;

/// Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
    pub use hyper::status::StatusClass;
}

#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotFound(String),
    InvalidConfig,
    InvalidRouterConfig,
    FileNotExist,
    ShouldRedirect(String),
    Fatal(String),
    Custom(String),
}
pub type Result<T> = ::std::result::Result<T, Error>;

pub trait SapperModule: Sync + Send {
    fn before(&self, req: &mut SapperRequest) -> Result<()> {
        Ok(())
    }

    fn after(&self, req: &SapperRequest, res: &mut SapperResponse) -> Result<()> {
        Ok(())
    }

    fn router(&self, &mut SapperRouter) -> Result<()>;

}

pub trait SapperAppShell {
    fn before(&self, &mut SapperRequest) -> Result<()>;
    fn after(&self, &SapperRequest, &mut SapperResponse) -> Result<()>;
}

pub type GlobalInitClosure = Box<Fn(&mut SapperRequest) -> Result<()> + 'static + Send + Sync>;
pub type SapperAppShellType = Box<SapperAppShell + 'static + Send + Sync>;

pub struct SapperApp {
    pub address:        String,
    pub port:           u32,
    // for app entry, global middeware
    pub shell:          Option<Arc<SapperAppShellType>>,
    // for actually use to recognize
    pub routers:        Router,
    // do simple static file service
    pub static_service: bool,

    pub init_closure:   Option<Arc<GlobalInitClosure>>
}

impl SapperApp {
    pub fn new() -> SapperApp {
        SapperApp {
            address: String::new(),
            port: 0,
            shell: None,
            routers: Router::new(),
            static_service: true,
            init_closure: None
        }
    }

    pub fn address(&mut self, address: &str) -> &mut Self {
        self.address = address.to_owned();
        self
    }

    pub fn port(&mut self, port: u32) -> &mut Self {
        self.port = port;
        self
    }

    pub fn static_service(&mut self, open: bool) -> &mut Self {
        self.static_service = open;
        self
    }

    pub fn with_shell(&mut self, w: SapperAppShellType) -> &mut Self {
        self.shell = Some(Arc::new(w));
        self
    }

    pub fn init_global(&mut self, clos: GlobalInitClosure) -> &mut Self {
        self.init_closure = Some(Arc::new(clos));
        self
    }

    // add methods of this sapper module
    pub fn add_module(&mut self, sm: Box<SapperModule>) -> &mut Self {

        let mut router = SapperRouter::new();
        // get the sm router
        sm.router(&mut router).unwrap();
        let sm = Arc::new(sm);

        for (method, handler_vec) in router.into_router() {
            // add to wrapped router
            for &(glob, ref handler) in handler_vec.iter() {
                let method = method.clone();
                let glob = glob.clone();
                let handler = handler.clone();
                let sm = sm.clone();
                let shell = self.shell.clone();
                let init_closure = self.init_closure.clone();

                self.routers.route(method, glob, Arc::new(Box::new(move |req: &mut SapperRequest| -> Result<SapperResponse> {
                    if let Some(ref c) = init_closure {
                        c(req)?;
                    }
                    if let Some(ref shell) = shell {
                        shell.before(req)?;
                    }
                    sm.before(req)?;
                    let mut response: SapperResponse = handler.handle(req)?;
                    sm.after(req, &mut response)?;
                    if let Some(ref shell) = shell {
                        shell.after(req, &mut response)?;
                    }
                    Ok(response)
                })));
            }
        }

        self
    }

    pub fn run_http(self) {

        let addr = self.address.clone() + ":" + &self.port.to_string();
        //let arc_sapp = Arc::new(Box::new(self));

        let self_box = Arc::new(Box::new(self));

        let server = Http::new().bind(
            &addr.parse().unwrap(),
            move || Ok(self_box.clone())
        ).unwrap();
        server.run().unwrap();
    }
}

impl Service for SapperApp {
    type Request = HyperRequest;
    type Response = HyperResponse;
    type Error = hyper::Error;
    type Future = FutureResult<Self::Response, hyper::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {

        // make request from hyper request
//        let mut sreq = SapperRequest::new(&req);
        let mut sreq = SapperRequest::new(Box::new(req));

        // pass req to routers, execute matched biz handler
        let response_w = self.routers.handle_method(&mut sreq).unwrap();
        if response_w.is_err() {
            let path = sreq.path();
            if self.static_service {
                match simple_file_get(path) {
                    Ok((file_u8vec, file_mime)) => {
                        let mut response = Self::Response::new()
                            .with_header(ContentLength(file_u8vec.len() as u64));

                        response.headers_mut().set_raw("Content-Type", vec![file_mime.as_bytes().to_vec()]);
                        response.set_body(file_u8vec);

                        return futures::future::ok(response);
                    },
                    Err(_) => {
                        // return 404 NotFound now
                        let response = Self::Response::new()
                            .with_status(StatusCode::NotFound);

                        return futures::future::ok(response);
                    }
                }
            }

            // return 404 NotFound now
            let response = Self::Response::new()
                .with_status(StatusCode::NotFound);

            return futures::future::ok(response);
        }

        let sres = response_w.unwrap();
        match sres.body_ref() {
            &Some(ref vec) => {
                // TODO: need to optimize for live time problem
                let tvec = vec.clone();
                // TODO: need copy all headers from SapperResponse
                let response = Self::Response::new()
                    .with_header(ContentLength(vec.len() as u64))
                    .with_body(tvec);

                // here, if can not match any router, we need check static file service
                // or response NotFound

                futures::future::ok(response)
            },
            &None => {
                let response = Self::Response::new()
                    .with_body("".as_bytes());

                // here, if can not match any router, we need check static file service
                // or response NotFound

                futures::future::ok(response)
            }
        }
    }
}

// this is very expensive in time
// should make it as global
lazy_static! {
    static ref MTYPES: MimeTypes = { MimeTypes::new().unwrap() };
}

fn simple_file_get(path: &str) -> Result<(Vec<u8>, String)> {
    let new_path;
    if &path[(path.len()-1)..] == "/" {
        new_path = "static/".to_owned() + path + "index.html";
    }
    else {
        new_path = "static/".to_owned() + path;
    }
    //println!("file path: {}", new_path);
    match File::open(&new_path) {
        Ok(ref mut file) => {
            let mut s: Vec<u8> = vec![];
            file.read_to_end(&mut s).unwrap_or(0);

            let mt_str = MTYPES.mime_for_path(Path::new(&new_path));

            Ok((s, mt_str.to_owned()))
        },
        Err(_) => Err(Error::FileNotExist)
    }
}

<commit_msg>copy all headers from SapperResponse to HyperResponse.<commit_after>use std::str;
use std::io::{self, Read, Write};
use std::fs::File;
use std::path::Path;
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::sync::Arc;
use std::clone::Clone;

use hyper;
use hyper::{Get, Post, StatusCode};
use hyper::header::{ContentLength, ContentType};

use hyper::server::{Http, Service};
use hyper::server::Request as HyperRequest;
use hyper::server::Response as HyperResponse;
use hyper::Method;
use hyper::HttpVersion;

use futures;
use futures::future::FutureResult;
use mime_types::Types as MimeTypes;

pub use typemap::Key;
pub use hyper::header::Headers;
pub use hyper::header;
pub use hyper::mime;
pub use request::SapperRequest;
pub use response::SapperResponse;
pub use router_m::Router;
pub use router::SapperRouter;
pub use handler::SapperHandler;

#[derive(Clone)]
pub struct PathParams;

/// Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
    pub use hyper::status::StatusClass;
}

#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotFound(String),
    InvalidConfig,
    InvalidRouterConfig,
    FileNotExist,
    ShouldRedirect(String),
    Fatal(String),
    Custom(String),
}
pub type Result<T> = ::std::result::Result<T, Error>;

pub trait SapperModule: Sync + Send {
    fn before(&self, req: &mut SapperRequest) -> Result<()> {
        Ok(())
    }

    fn after(&self, req: &SapperRequest, res: &mut SapperResponse) -> Result<()> {
        Ok(())
    }

    fn router(&self, &mut SapperRouter) -> Result<()>;

}

pub trait SapperAppShell {
    fn before(&self, &mut SapperRequest) -> Result<()>;
    fn after(&self, &SapperRequest, &mut SapperResponse) -> Result<()>;
}

pub type GlobalInitClosure = Box<Fn(&mut SapperRequest) -> Result<()> + 'static + Send + Sync>;
pub type SapperAppShellType = Box<SapperAppShell + 'static + Send + Sync>;

pub struct SapperApp {
    pub address:        String,
    pub port:           u32,
    // for app entry, global middeware
    pub shell:          Option<Arc<SapperAppShellType>>,
    // for actually use to recognize
    pub routers:        Router,
    // do simple static file service
    pub static_service: bool,

    pub init_closure:   Option<Arc<GlobalInitClosure>>
}

impl SapperApp {
    pub fn new() -> SapperApp {
        SapperApp {
            address: String::new(),
            port: 0,
            shell: None,
            routers: Router::new(),
            static_service: true,
            init_closure: None
        }
    }

    pub fn address(&mut self, address: &str) -> &mut Self {
        self.address = address.to_owned();
        self
    }

    pub fn port(&mut self, port: u32) -> &mut Self {
        self.port = port;
        self
    }

    pub fn static_service(&mut self, open: bool) -> &mut Self {
        self.static_service = open;
        self
    }

    pub fn with_shell(&mut self, w: SapperAppShellType) -> &mut Self {
        self.shell = Some(Arc::new(w));
        self
    }

    pub fn init_global(&mut self, clos: GlobalInitClosure) -> &mut Self {
        self.init_closure = Some(Arc::new(clos));
        self
    }

    // add methods of this sapper module
    pub fn add_module(&mut self, sm: Box<SapperModule>) -> &mut Self {

        let mut router = SapperRouter::new();
        // get the sm router
        sm.router(&mut router).unwrap();
        let sm = Arc::new(sm);

        for (method, handler_vec) in router.into_router() {
            // add to wrapped router
            for &(glob, ref handler) in handler_vec.iter() {
                let method = method.clone();
                let glob = glob.clone();
                let handler = handler.clone();
                let sm = sm.clone();
                let shell = self.shell.clone();
                let init_closure = self.init_closure.clone();

                self.routers.route(method, glob, Arc::new(Box::new(move |req: &mut SapperRequest| -> Result<SapperResponse> {
                    if let Some(ref c) = init_closure {
                        c(req)?;
                    }
                    if let Some(ref shell) = shell {
                        shell.before(req)?;
                    }
                    sm.before(req)?;
                    let mut response: SapperResponse = handler.handle(req)?;
                    sm.after(req, &mut response)?;
                    if let Some(ref shell) = shell {
                        shell.after(req, &mut response)?;
                    }
                    Ok(response)
                })));
            }
        }

        self
    }

    pub fn run_http(self) {

        let addr = self.address.clone() + ":" + &self.port.to_string();
        //let arc_sapp = Arc::new(Box::new(self));

        let self_box = Arc::new(Box::new(self));

        let server = Http::new().bind(
            &addr.parse().unwrap(),
            move || Ok(self_box.clone())
        ).unwrap();
        server.run().unwrap();
    }
}

impl Service for SapperApp {
    type Request = HyperRequest;
    type Response = HyperResponse;
    type Error = hyper::Error;
    type Future = FutureResult<Self::Response, hyper::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {

        // make request from hyper request
//        let mut sreq = SapperRequest::new(&req);
        let mut sreq = SapperRequest::new(Box::new(req));

        // pass req to routers, execute matched biz handler
        let response_w = self.routers.handle_method(&mut sreq).unwrap();
        if response_w.is_err() {
            let path = sreq.path();
            if self.static_service {
                match simple_file_get(path) {
                    Ok((file_u8vec, file_mime)) => {
                        let mut response = Self::Response::new()
                            .with_header(ContentLength(file_u8vec.len() as u64));

                        response.headers_mut().set_raw("Content-Type", vec![file_mime.as_bytes().to_vec()]);
                        response.set_body(file_u8vec);

                        return futures::future::ok(response);
                    },
                    Err(_) => {
                        // return 404 NotFound now
                        let response = Self::Response::new()
                            .with_status(StatusCode::NotFound);

                        return futures::future::ok(response);
                    }
                }
            }

            // return 404 NotFound now
            let response = Self::Response::new()
                .with_status(StatusCode::NotFound);

            return futures::future::ok(response);
        }

        let sres = response_w.unwrap();
        match sres.body_ref() {
            &Some(ref vec) => {
                let mut response = Self::Response::new();

                for header in sres.headers().iter() {
                    response.headers_mut()
                        .set_raw(header.name().to_owned(),
                            vec![header.value_string().as_bytes().to_vec()]);
                }

                response.headers_mut().set(ContentLength(vec.len() as u64));
                // TODO: need to optimize for live time problem
                let tvec = vec.clone();

                response.set_body(tvec);

                // here, if can not match any router, we need check static file service
                // or response NotFound

                futures::future::ok(response)
            },
            &None => {
                let response = Self::Response::new()
                    .with_body("".as_bytes());

                // here, if can not match any router, we need check static file service
                // or response NotFound

                futures::future::ok(response)
            }
        }
    }
}

// this is very expensive in time
// should make it as global
lazy_static! {
    static ref MTYPES: MimeTypes = { MimeTypes::new().unwrap() };
}

fn simple_file_get(path: &str) -> Result<(Vec<u8>, String)> {
    let new_path;
    if &path[(path.len()-1)..] == "/" {
        new_path = "static/".to_owned() + path + "index.html";
    }
    else {
        new_path = "static/".to_owned() + path;
    }
    //println!("file path: {}", new_path);
    match File::open(&new_path) {
        Ok(ref mut file) => {
            let mut s: Vec<u8> = vec![];
            file.read_to_end(&mut s).unwrap_or(0);

            let mt_str = MTYPES.mime_for_path(Path::new(&new_path));

            Ok((s, mt_str.to_owned()))
        },
        Err(_) => Err(Error::FileNotExist)
    }
}

<|endoftext|>


<commit_before>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use super::allocator::Allocator;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new<T: Allocator>(proto: JsProto, name: &str,
                             kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
                             allocator: &mut T) -> JsObjStruct {
        JsObjStruct {
            proto: None,
            name: String::from(name),
            dict: kv_tuples.into_iter().map(|(k, v, ptr)| {
                if let Some(ptr) = ptr {
                    allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key<T: Allocator>(&mut self, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut T) {
        // If the key already exists, potentially condemn its pointer, which is being overwritten.
        if let Some(var) = self.dict.get(&k) {
            println!("VAR");
            match var.t {
                JsType::JsPtr(_) => { println!("CONDEMN"); allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        // Then, allocate the new pointer if necessary...
        if let Some(ptr) = ptr {
            println!("POINTER");
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }
        // ...and insert the key & value into the dictionary blindly.
        self.dict.insert(k, v);
        println!("DONE");
        println!("{:#?}", self);
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}

<commit_msg>More dumb debug prints because of version conflicts<commit_after>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use super::allocator::Allocator;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new<T: Allocator>(proto: JsProto, name: &str,
                             kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
                             allocator: &mut T) -> JsObjStruct {
        JsObjStruct {
            proto: None,
            name: String::from(name),
            dict: kv_tuples.into_iter().map(|(k, v, ptr)| {
                if let Some(ptr) = ptr {
                    allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key<T: Allocator>(&mut self, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut T) {
        println!("{:#?}", self);
        // If the key already exists, potentially condemn its pointer, which is being overwritten.
        if let Some(var) = self.dict.get(&k) {
            println!("VAR");
            match var.t {
                JsType::JsPtr(_) => { println!("CONDEMN"); allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        // Then, allocate the new pointer if necessary...
        if let Some(ptr) = ptr {
            println!("POINTER");
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }
        // ...and insert the key & value into the dictionary blindly.
        self.dict.insert(k, v);
        println!("DONE");
        println!("{:#?}", self);
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}

<|endoftext|>


<commit_before>//! JMESPath AST

use std::fmt;

use super::lexer::Token;
use super::RcVar;
use super::Coordinates;

/// Abstract syntax tree of a JMESPath expression.
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum Ast {
    /// Compares two nodes using a comparator, returning true/false.
    Comparison {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Comparator that compares the two results
        comparator: Comparator,
        /// Left hand side of the comparison
        lhs: Box<Ast>,
        /// Right hand side of the comparison
        rhs: Box<Ast>,
    },
    /// If `predicate` evaluates to a truthy value, returns the
    /// result `then`
    Condition {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// The predicate to test.
        predicate: Box<Ast>,
        /// The node to traverse if the predicate is truthy.
        then: Box<Ast>,
    },
    /// Returns the current node.
    Identity {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
    },
    /// Used by functions to dynamically evaluate argument values.
    Expref {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to execute
        ast: Box<Ast>,
    },
    /// Evaluates the node, then flattens it one level.
    Flatten {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to execute and flatten
        node: Box<Ast>,
    },
    /// Function name and a vec or function argument expressions.
    Function {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Function name to invoke.
        name: String,
        /// Function arguments.
        args: Vec<Ast>,
    },
    /// Extracts a key by name from a map.
    Field {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Field name to extract.
        name: String,
    },
    /// Extracts an index from a Vec.
    Index {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Index to extract
        idx: i32,
    },
    /// Resolves to a literal value.
    Literal {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Literal value
        value: RcVar,
    },
    /// Evaluates to a list of evaluated expressions.
    MultiList {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Elements of the list
        elements: Vec<Ast>,
    },
    /// Evaluates to a map of key value pairs.
    MultiHash {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Elements of the hash
        elements: Vec<KeyValuePair>,
    },
    /// Evaluates to true/false based on if the expression is not truthy.
    Not {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// node to negate
        node: Box<Ast>,
    },
    /// Evaluates LHS, and pushes each value through RHS.
    Projection {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the projection.
        lhs: Box<Ast>,
        /// Right hand side of the projection.
        rhs: Box<Ast>,
    },
    /// Evaluates LHS. If it resolves to an object, returns a Vec of values.
    ObjectValues {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to extract object values from.
        node: Box<Ast>,
    },
    /// Evaluates LHS. If not truthy returns. Otherwise evaluates RHS.
    And {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
    /// Evaluates LHS. If truthy returns. Otherwise evaluates RHS.
    Or {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
    /// Returns a slice of a vec, using start, stop, and step.
    Slice {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Starting index
        start: Option<i32>,
        /// Stopping index
        stop: Option<i32>,
        /// Step amount between extractions.
        step: i32,
    },
    /// Evaluates RHS, then provides that value to the evaluation of RHS.
    Subexpr {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
}

impl Ast {
    /// Create a coordinates struct for the AST node.
    pub fn make_coordinates(&self, expr: &str) -> Coordinates {
        let offset = match self {
            &Ast::Comparison { offset, .. } => offset,
            &Ast::Condition { offset, .. } => offset,
            &Ast::Identity { offset, .. } => offset,
            &Ast::Expref { offset, .. } => offset,
            &Ast::Flatten { offset, .. } => offset,
            &Ast::Function { offset, .. } => offset,
            &Ast::Field { offset, .. } => offset,
            &Ast::Index { offset, .. } => offset,
            &Ast::Literal { offset, .. } => offset,
            &Ast::MultiList { offset, .. } => offset,
            &Ast::MultiHash { offset, .. } => offset,
            &Ast::Not { offset, .. } => offset,
            &Ast::Projection { offset, .. } => offset,
            &Ast::ObjectValues { offset, .. } => offset,
            &Ast::And { offset, .. } => offset,
            &Ast::Or { offset, .. } => offset,
            &Ast::Slice { offset, .. } => offset,
            &Ast::Subexpr { offset, .. } => offset,
        };
        Coordinates::from_offset(expr, offset)
    }
}

impl fmt::Display for Ast {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:#?}", self)
    }
}

/// Represents a key value pair in a multi-hash
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub struct KeyValuePair {
    /// Key expression used to determine the key. This expression must
    /// resolve to a string variable.
    pub key: Ast,
    /// Value expression used to determine the value.
    pub value: Ast
}

/// Comparators used in Comparison nodes
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum Comparator {
    /// Equivalance (e.g., `==`)
    Eq,
    /// Less than (e.g., `<`)
    Lt,
    /// Less than or equal to (e.g., `<=`)
    Lte,
    /// Not equal (e.g., `!=`)
    Ne,
    /// Greater than (e.g., `>`)
    Gt,
    /// Greater than or equal to (e.g., `>=`)
    Gte,
}

impl Comparator {
    /// Get the binding power of the operator.
    pub fn lbp(&self) -> usize {
        match self {
            &Comparator::Eq => Token::Eq.lbp(),
            &Comparator::Ne => Token::Ne.lbp(),
            &Comparator::Lt => Token::Lt.lbp(),
            &Comparator::Lte => Token::Lte.lbp(),
            &Comparator::Gt => Token::Gt.lbp(),
            &Comparator::Gte => Token::Gte.lbp(),
        }
    }
}
<commit_msg>Adding Ast code coverage<commit_after>//! JMESPath AST

use std::fmt;

use super::lexer::Token;
use super::RcVar;
use super::Coordinates;

/// Abstract syntax tree of a JMESPath expression.
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum Ast {
    /// Compares two nodes using a comparator, returning true/false.
    Comparison {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Comparator that compares the two results
        comparator: Comparator,
        /// Left hand side of the comparison
        lhs: Box<Ast>,
        /// Right hand side of the comparison
        rhs: Box<Ast>,
    },
    /// If `predicate` evaluates to a truthy value, returns the
    /// result `then`
    Condition {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// The predicate to test.
        predicate: Box<Ast>,
        /// The node to traverse if the predicate is truthy.
        then: Box<Ast>,
    },
    /// Returns the current node.
    Identity {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
    },
    /// Used by functions to dynamically evaluate argument values.
    Expref {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to execute
        ast: Box<Ast>,
    },
    /// Evaluates the node, then flattens it one level.
    Flatten {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to execute and flatten
        node: Box<Ast>,
    },
    /// Function name and a vec or function argument expressions.
    Function {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Function name to invoke.
        name: String,
        /// Function arguments.
        args: Vec<Ast>,
    },
    /// Extracts a key by name from a map.
    Field {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Field name to extract.
        name: String,
    },
    /// Extracts an index from a Vec.
    Index {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Index to extract
        idx: i32,
    },
    /// Resolves to a literal value.
    Literal {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Literal value
        value: RcVar,
    },
    /// Evaluates to a list of evaluated expressions.
    MultiList {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Elements of the list
        elements: Vec<Ast>,
    },
    /// Evaluates to a map of key value pairs.
    MultiHash {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Elements of the hash
        elements: Vec<KeyValuePair>,
    },
    /// Evaluates to true/false based on if the expression is not truthy.
    Not {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// node to negate
        node: Box<Ast>,
    },
    /// Evaluates LHS, and pushes each value through RHS.
    Projection {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the projection.
        lhs: Box<Ast>,
        /// Right hand side of the projection.
        rhs: Box<Ast>,
    },
    /// Evaluates LHS. If it resolves to an object, returns a Vec of values.
    ObjectValues {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Node to extract object values from.
        node: Box<Ast>,
    },
    /// Evaluates LHS. If not truthy returns. Otherwise evaluates RHS.
    And {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
    /// Evaluates LHS. If truthy returns. Otherwise evaluates RHS.
    Or {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
    /// Returns a slice of a vec, using start, stop, and step.
    Slice {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Starting index
        start: Option<i32>,
        /// Stopping index
        stop: Option<i32>,
        /// Step amount between extractions.
        step: i32,
    },
    /// Evaluates RHS, then provides that value to the evaluation of RHS.
    Subexpr {
        /// Approximate absolute position in the parsed expression.
        offset: usize,
        /// Left hand side of the expression.
        lhs: Box<Ast>,
        /// Right hand side of the expression.
        rhs: Box<Ast>,
    },
}

impl Ast {
    /// Create a coordinates struct for the AST node.
    pub fn make_coordinates(&self, expr: &str) -> Coordinates {
        let offset = match self {
            &Ast::Comparison { offset, .. } => offset,
            &Ast::Condition { offset, .. } => offset,
            &Ast::Identity { offset, .. } => offset,
            &Ast::Expref { offset, .. } => offset,
            &Ast::Flatten { offset, .. } => offset,
            &Ast::Function { offset, .. } => offset,
            &Ast::Field { offset, .. } => offset,
            &Ast::Index { offset, .. } => offset,
            &Ast::Literal { offset, .. } => offset,
            &Ast::MultiList { offset, .. } => offset,
            &Ast::MultiHash { offset, .. } => offset,
            &Ast::Not { offset, .. } => offset,
            &Ast::Projection { offset, .. } => offset,
            &Ast::ObjectValues { offset, .. } => offset,
            &Ast::And { offset, .. } => offset,
            &Ast::Or { offset, .. } => offset,
            &Ast::Slice { offset, .. } => offset,
            &Ast::Subexpr { offset, .. } => offset,
        };
        Coordinates::from_offset(expr, offset)
    }
}

impl fmt::Display for Ast {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:#?}", self)
    }
}

/// Represents a key value pair in a multi-hash
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub struct KeyValuePair {
    /// Key expression used to determine the key. This expression must
    /// resolve to a string variable.
    pub key: Ast,
    /// Value expression used to determine the value.
    pub value: Ast
}

/// Comparators used in Comparison nodes
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub enum Comparator {
    /// Equivalance (e.g., `==`)
    Eq,
    /// Less than (e.g., `<`)
    Lt,
    /// Less than or equal to (e.g., `<=`)
    Lte,
    /// Not equal (e.g., `!=`)
    Ne,
    /// Greater than (e.g., `>`)
    Gt,
    /// Greater than or equal to (e.g., `>=`)
    Gte,
}

impl Comparator {
    /// Get the binding power of the operator.
    pub fn lbp(&self) -> usize {
        match self {
            &Comparator::Eq => Token::Eq.lbp(),
            &Comparator::Ne => Token::Ne.lbp(),
            &Comparator::Lt => Token::Lt.lbp(),
            &Comparator::Lte => Token::Lte.lbp(),
            &Comparator::Gt => Token::Gt.lbp(),
            &Comparator::Gte => Token::Gte.lbp(),
        }
    }
}

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

    #[test]
    fn makes_coordinates_from_ast_node() {
        let expr = "foo.abc";
        let node = Ast::Field { name: "abc".to_string(), offset: 4 };
        let coords = node.make_coordinates(expr);
        assert_eq!(0, coords.line);
        assert_eq!(4, coords.column);
        assert_eq!(4, coords.offset);
    }

    #[test]
    fn displays_pretty_printed_ast_node() {
        let node = Ast::Field { name: "abc".to_string(), offset: 4 };
        assert_eq!("Field {\n    offset: 4,\n    name: \"abc\"\n}", format!("{}", node));
    }

    #[test]
    fn gets_comparator_lbp() {
        assert_eq!(Token::Eq.lbp(), Comparator::Eq.lbp());
        assert_eq!(Token::Ne.lbp(), Comparator::Ne.lbp());
        assert_eq!(Token::Gt.lbp(), Comparator::Gt.lbp());
        assert_eq!(Token::Gte.lbp(), Comparator::Gte.lbp());
        assert_eq!(Token::Lt.lbp(), Comparator::Lt.lbp());
        assert_eq!(Token::Lte.lbp(), Comparator::Lte.lbp());
    }
}
<|endoftext|>