<commit_before>use crate::error::*;

use crate::buffer::Buffered;
use std::any::Any;
use std::io::SeekFrom;
use std::sync::Arc;

use crate::common::*;

use crate::data::packet::Packet;
use crate::stream::Stream;

#[derive(Clone, Debug)]
pub enum Event {
    NewPacket(Packet),
    NewStream(Stream),
    MoreDataNeeded(usize),
    Eof,
}

pub trait Demuxer: Send {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom>;
    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)>;
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<dyn Demuxer>;
    fn describe(&self) -> &Descr;

    fn probe(&self, data: &[u8]) -> u8;
}

pub struct Context {
    demuxer: Box<dyn Demuxer>,
    reader: Box<dyn Buffered>,
    pub info: GlobalInfo,
    pub user_private: Option<Arc<dyn Any + Send + Sync>>,
}

impl Context {
    pub fn new<R: Buffered + 'static>(demuxer: Box<dyn Demuxer>, reader: Box<R>) -> Self {
        Context {
            demuxer,
            reader,
            info: GlobalInfo {
                duration: None,
                timebase: None,
                streams: Vec::with_capacity(2),
            },
            user_private: None,
        }
    }

    fn read_headers_internal(&mut self) -> Result<()> {
        let demux = &mut self.demuxer;

        let res = demux.read_headers(&self.reader, &mut self.info);
        match res {
            Err(e) => Err(e),
            Ok(seek) => {
                //TODO: handle seeking here
                let res = self.reader.seek(seek);
                trace!("stream now at index: {:?}", res);
                Ok(())
            }
        }
    }

    pub fn read_headers(&mut self) -> Result<()> {
        loop {
            // TODO: wrap fill_buf() with a check for Eof
            self.reader.fill_buf()?;
            match self.read_headers_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        self.reader.grow(needed);
                    }
                    _ => return Err(e),
                },
                Ok(_) => return Ok(()),
            }
        }
    }

    fn read_event_internal(&mut self) -> Result<Event> {
        let demux = &mut self.demuxer;

        let res = demux.read_event(&self.reader);
        match res {
            Err(e) => Err(e),
            Ok((seek, mut event)) => {
                //TODO: handle seeking here
                let _ = self.reader.seek(seek)?;
                if let Event::NewStream(ref st) = event {
                    self.info.streams.push(st.clone());
                }
                if let Event::MoreDataNeeded(size) = event {
                    return Err(Error::MoreDataNeeded(size));
                }
                if let Event::NewPacket(ref mut pkt) = event {
                    if pkt.t.timebase.is_none() {
                        if let Some(ref st) = self
                            .info
                            .streams
                            .iter()
                            .find(|s| s.index as isize == pkt.stream_index)
                        {
                            pkt.t.timebase = Some(st.timebase);
                        }
                    }
                }
                Ok(event)
            }
        }
    }

    pub fn read_event(&mut self) -> Result<Event> {
        // TODO: guard against infiniloops and maybe factor the loop.
        loop {
            match self.read_event_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        let len = self.reader.data().len();

                        // we might have sent MoreDatNeeded(0) to request a new call
                        if len >= needed {
                          println!("available length {} >= requested needed {}, continuing", len, needed);
                          continue
                        }
                        self.reader.grow(needed);
                        self.reader.fill_buf()?;
                        if self.reader.data().len() <= len {
                            return Ok(Event::Eof);
                        }
                    }
                    _ => return Err(e),
                },
                Ok(ev) => return Ok(ev),
            }
        }
    }
}

pub const PROBE_DATA: usize = 4 * 1024;
pub const PROBE_SCORE_EXTENSION: u8 = 50;

// TODO:
// IntoIterator<Item = &'static Descriptor> is confusing

pub trait Probe {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor>;
}

impl<'a> Probe for [&'static dyn Descriptor] {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor> {
        let mut max = u8::min_value();
        let mut candidate: Option<&'static dyn Descriptor> = None;
        for desc in self {
            let score = desc.probe(data);

            if score > max {
                max = score;
                candidate = Some(*desc);
            }
        }

        if max > PROBE_SCORE_EXTENSION {
            candidate
        } else {
            None
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::data::packet::Packet;
    use std::io::SeekFrom;

    struct DummyDes {
        d: Descr,
    }

    struct DummyDemuxer {}

    impl Demuxer for DummyDemuxer {
        fn read_headers(
            &mut self,
            buf: &Box<&dyn Buffered>,
            _info: &mut GlobalInfo,
        ) -> Result<SeekFrom> {
            let len = buf.data().len();
            if 9 > len {
                let needed = 9 - len;
                Err(Error::MoreDataNeeded(needed))
            } else {
                Ok(SeekFrom::Current(9))
            }
        }
        fn read_event(&mut self, buf: &Box<&dyn Buffered>) -> Result<(SeekFrom, Event)> {
            let size = 2;
            let len = buf.data().len();
            if size > len {
                Err(Error::MoreDataNeeded(size - len))
            } else {
                debug!("{:?}", buf.data());
                match &buf.data()[..2] {
                    b"p1" => Ok((SeekFrom::Current(3), Event::NewPacket(Packet::new()))),
                    b"e1" => Ok((SeekFrom::Current(3), Event::MoreDataNeeded(0))),
                    _ => Err(Error::InvalidData.into()),
                }
            }
        }
    }

    impl Descriptor for DummyDes {
        fn create(&self) -> Box<dyn Demuxer> {
            Box::new(DummyDemuxer {})
        }
        fn describe<'a>(&'a self) -> &'a Descr {
            &self.d
        }
        fn probe(&self, data: &[u8]) -> u8 {
            match data {
                b"dummy" => 100,
                _ => 0,
            }
        }
    }

    const DUMMY_DES: &dyn Descriptor = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy dem",
            extensions: &["dm", "dum"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn probe() {
        let demuxers: &[&'static dyn Descriptor] = &[DUMMY_DES];

        demuxers.probe(b"dummy").unwrap();
    }

    use crate::buffer::*;
    use std::io::Cursor;

    #[test]
    fn read_headers() {
        let buf = b"dummy header";
        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();
    }

    #[test]
    fn read_event() {
        let buf = b"dummy header p1 e1 p1 ";

        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();

        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
    }
}
<commit_msg>fix test compilation<commit_after>use crate::error::*;

use crate::buffer::Buffered;
use std::any::Any;
use std::io::SeekFrom;
use std::sync::Arc;

use crate::common::*;

use crate::data::packet::Packet;
use crate::stream::Stream;

#[derive(Clone, Debug)]
pub enum Event {
    NewPacket(Packet),
    NewStream(Stream),
    MoreDataNeeded(usize),
    Eof,
}

pub trait Demuxer: Send {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom>;
    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)>;
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<dyn Demuxer>;
    fn describe(&self) -> &Descr;

    fn probe(&self, data: &[u8]) -> u8;
}

pub struct Context {
    demuxer: Box<dyn Demuxer>,
    reader: Box<dyn Buffered>,
    pub info: GlobalInfo,
    pub user_private: Option<Arc<dyn Any + Send + Sync>>,
}

impl Context {
    pub fn new<R: Buffered + 'static>(demuxer: Box<dyn Demuxer>, reader: Box<R>) -> Self {
        Context {
            demuxer,
            reader,
            info: GlobalInfo {
                duration: None,
                timebase: None,
                streams: Vec::with_capacity(2),
            },
            user_private: None,
        }
    }

    fn read_headers_internal(&mut self) -> Result<()> {
        let demux = &mut self.demuxer;

        let res = demux.read_headers(&self.reader, &mut self.info);
        match res {
            Err(e) => Err(e),
            Ok(seek) => {
                //TODO: handle seeking here
                let res = self.reader.seek(seek);
                trace!("stream now at index: {:?}", res);
                Ok(())
            }
        }
    }

    pub fn read_headers(&mut self) -> Result<()> {
        loop {
            // TODO: wrap fill_buf() with a check for Eof
            self.reader.fill_buf()?;
            match self.read_headers_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        self.reader.grow(needed);
                    }
                    _ => return Err(e),
                },
                Ok(_) => return Ok(()),
            }
        }
    }

    fn read_event_internal(&mut self) -> Result<Event> {
        let demux = &mut self.demuxer;

        let res = demux.read_event(&self.reader);
        match res {
            Err(e) => Err(e),
            Ok((seek, mut event)) => {
                //TODO: handle seeking here
                let _ = self.reader.seek(seek)?;
                if let Event::NewStream(ref st) = event {
                    self.info.streams.push(st.clone());
                }
                if let Event::MoreDataNeeded(size) = event {
                    return Err(Error::MoreDataNeeded(size));
                }
                if let Event::NewPacket(ref mut pkt) = event {
                    if pkt.t.timebase.is_none() {
                        if let Some(ref st) = self
                            .info
                            .streams
                            .iter()
                            .find(|s| s.index as isize == pkt.stream_index)
                        {
                            pkt.t.timebase = Some(st.timebase);
                        }
                    }
                }
                Ok(event)
            }
        }
    }

    pub fn read_event(&mut self) -> Result<Event> {
        // TODO: guard against infiniloops and maybe factor the loop.
        loop {
            match self.read_event_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        let len = self.reader.data().len();

                        // we might have sent MoreDatNeeded(0) to request a new call
                        if len >= needed {
                          println!("available length {} >= requested needed {}, continuing", len, needed);
                          continue
                        }
                        self.reader.grow(needed);
                        self.reader.fill_buf()?;
                        if self.reader.data().len() <= len {
                            return Ok(Event::Eof);
                        }
                    }
                    _ => return Err(e),
                },
                Ok(ev) => return Ok(ev),
            }
        }
    }
}

pub const PROBE_DATA: usize = 4 * 1024;
pub const PROBE_SCORE_EXTENSION: u8 = 50;

// TODO:
// IntoIterator<Item = &'static Descriptor> is confusing

pub trait Probe {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor>;
}

impl<'a> Probe for [&'static dyn Descriptor] {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor> {
        let mut max = u8::min_value();
        let mut candidate: Option<&'static dyn Descriptor> = None;
        for desc in self {
            let score = desc.probe(data);

            if score > max {
                max = score;
                candidate = Some(*desc);
            }
        }

        if max > PROBE_SCORE_EXTENSION {
            candidate
        } else {
            None
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::data::packet::Packet;
    use std::io::SeekFrom;

    struct DummyDes {
        d: Descr,
    }

    struct DummyDemuxer {}

    impl Demuxer for DummyDemuxer {
        fn read_headers(
            &mut self,
            buf: &Box<dyn Buffered>,
            _info: &mut GlobalInfo,
        ) -> Result<SeekFrom> {
            let len = buf.data().len();
            if 9 > len {
                let needed = 9 - len;
                Err(Error::MoreDataNeeded(needed))
            } else {
                Ok(SeekFrom::Current(9))
            }
        }
        fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)> {
            let size = 2;
            let len = buf.data().len();
            if size > len {
                Err(Error::MoreDataNeeded(size - len))
            } else {
                debug!("{:?}", buf.data());
                match &buf.data()[..2] {
                    b"p1" => Ok((SeekFrom::Current(3), Event::NewPacket(Packet::new()))),
                    b"e1" => Ok((SeekFrom::Current(3), Event::MoreDataNeeded(0))),
                    _ => Err(Error::InvalidData.into()),
                }
            }
        }
    }

    impl Descriptor for DummyDes {
        fn create(&self) -> Box<dyn Demuxer> {
            Box::new(DummyDemuxer {})
        }
        fn describe<'a>(&'a self) -> &'a Descr {
            &self.d
        }
        fn probe(&self, data: &[u8]) -> u8 {
            match data {
                b"dummy" => 100,
                _ => 0,
            }
        }
    }

    const DUMMY_DES: &dyn Descriptor = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy dem",
            extensions: &["dm", "dum"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn probe() {
        let demuxers: &[&'static dyn Descriptor] = &[DUMMY_DES];

        demuxers.probe(b"dummy").unwrap();
    }

    use crate::buffer::*;
    use std::io::Cursor;

    #[test]
    fn read_headers() {
        let buf = b"dummy header";
        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();
    }

    #[test]
    fn read_event() {
        let buf = b"dummy header p1 e1 p1 ";

        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();

        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
    }
}
<|endoftext|>


<commit_before>/// for the moment we keep the old way of formating the labels, but this could change
/// the current format is '{nice name} ({city})'
/// the {nice name} being for addresses the housenumber and the street (correctly ordered)
/// and for the rest of the objects, only their names
fn format_label<'a>(
    nice_name: String,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
) -> String {
    let city = admins.clone().find(|adm| adm.is_city());
    let city_name = city.map(|a| a.name.to_string());

    match city_name {
        Some(n) => format!("{} ({})", nice_name, n),
        None => nice_name.to_string(),
    }
}

// Note: even if most of the format methods are the same for the moment,
// I feel it's better to split them to make them easier to update

/// format a label for a Street
pub fn format_street_label<'a>(
    name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    _country_codes: &[String], // Note: for the moment the country code is not used, but this could change
) -> String {
    format_label(name.to_owned(), admins)
}

/// format a label for a Poi
pub fn format_poi_label<'a>(
    name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    _country_codes: &[String],
) -> String {
    format_label(name.to_owned(), admins)
}

/// format a name and a label for an Address
pub fn format_addr_name_and_label<'a>(
    house_number: &str,
    street_name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    country_codes: &[String],
) -> (String, String) {
    let place = FormatPlaceHolder::from_addr(house_number.to_owned(), street_name.to_owned());
    let nice_name = get_short_addr_label(place, admins.clone(), country_codes);

    (nice_name.clone(), format_label(nice_name, admins))
}

pub fn get_short_addr_label<'a>(
    place: FormatPlaceHolder,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    country_codes: &[String],
) -> String {
    let country_code = country_codes.iter().next().map(|c| c.to_string()); // we arbitrarily take the first country code
    address_formatter::FORMATTER
        .short_addr_format_with_config(
            place.into_place(admins),
            address_formatter::Configuration {
                country_code,
                ..Default::default()
            },
        )
        .map_err(|e| warn!("impossible to format label: {}", e))
        .unwrap_or_else(|_| "".to_owned())
}

pub struct FormatPlaceHolder {
    street: String,
    // zip_code: Vec<String>, // For the moment we don't put the zip code in the label
    house_number: Option<String>,
}

impl FormatPlaceHolder {
    pub fn from_addr(house_number: String, street: String) -> Self {
        Self {
            street,
            house_number: Some(house_number),
        }
    }
    pub fn from_street(street: String) -> Self {
        Self {
            street,
            house_number: None,
        }
    }

    pub fn into_place<'b>(
        self,
        admins: impl Iterator<Item = &'b mimir::Admin>,
    ) -> address_formatter::Place {
        use address_formatter::Component;
        let mut place = address_formatter::Place::default();
        place[Component::HouseNumber] = self.house_number;
        place[Component::Road] = Some(self.street);

        for a in admins {
            if let Some(addr_equivalent) = cosmo_to_addr_formatter_type(&a.zone_type) {
                place[addr_equivalent] = Some(a.name.clone());
            }
        }
        place
    }
}

fn cosmo_to_addr_formatter_type(
    cosmo_type: &Option<cosmogony::ZoneType>,
) -> Option<address_formatter::Component> {
    use address_formatter::Component;
    match cosmo_type {
        Some(cosmogony::ZoneType::City) => Some(Component::City),
        Some(cosmogony::ZoneType::Country) => Some(Component::Country),
        Some(cosmogony::ZoneType::State) => Some(Component::State),
        Some(cosmogony::ZoneType::Suburb) => Some(Component::Suburb),
        // not sure, but it seems a cosmogony::StateDistrict is a County in address_formatter
        Some(cosmogony::ZoneType::StateDistrict) => Some(Component::County),
        _ => None,
    }
}
<commit_msg>add some tests<commit_after>/// for the moment we keep the old way of formating the labels, but this could change
/// the current format is '{nice name} ({city})'
/// the {nice name} being for addresses the housenumber and the street (correctly ordered)
/// and for the rest of the objects, only their names
fn format_label<'a>(
    nice_name: String,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
) -> String {
    let city = admins.clone().find(|adm| adm.is_city());
    let city_name = city.map(|a| a.name.to_string());

    match city_name {
        Some(n) => format!("{} ({})", nice_name, n),
        None => nice_name.to_string(),
    }
}

// Note: even if most of the format methods are the same for the moment,
// I feel it's better to split them to make them easier to update

/// format a label for a Street
pub fn format_street_label<'a>(
    name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    _country_codes: &[String], // Note: for the moment the country code is not used, but this could change
) -> String {
    format_label(name.to_owned(), admins)
}

/// format a label for a Poi
pub fn format_poi_label<'a>(
    name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    _country_codes: &[String],
) -> String {
    format_label(name.to_owned(), admins)
}

/// format a name and a label for an Address
pub fn format_addr_name_and_label<'a>(
    house_number: &str,
    street_name: &str,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    country_codes: &[String],
) -> (String, String) {
    let place = FormatPlaceHolder::from_addr(house_number.to_owned(), street_name.to_owned());
    let nice_name = get_short_addr_label(place, admins.clone(), country_codes);

    (nice_name.clone(), format_label(nice_name, admins))
}

fn get_short_addr_label<'a>(
    place: FormatPlaceHolder,
    admins: impl Iterator<Item = &'a mimir::Admin> + Clone,
    country_codes: &[String],
) -> String {
    let country_code = country_codes.iter().next().map(|c| c.to_string()); // we arbitrarily take the first country code
    address_formatter::FORMATTER
        .short_addr_format_with_config(
            place.into_place(admins),
            address_formatter::Configuration {
                country_code,
                ..Default::default()
            },
        )
        .map_err(|e| warn!("impossible to format label: {}", e))
        .unwrap_or_else(|_| "".to_owned())
}

struct FormatPlaceHolder {
    street: String,
    // zip_code: Vec<String>, // For the moment we don't put the zip code in the label
    house_number: Option<String>,
}

impl FormatPlaceHolder {
    pub fn from_addr(house_number: String, street: String) -> Self {
        Self {
            street,
            house_number: Some(house_number),
        }
    }

    pub fn into_place<'b>(
        self,
        admins: impl Iterator<Item = &'b mimir::Admin>,
    ) -> address_formatter::Place {
        use address_formatter::Component;
        let mut place = address_formatter::Place::default();
        place[Component::HouseNumber] = self.house_number;
        place[Component::Road] = Some(self.street);

        for a in admins {
            if let Some(addr_equivalent) = cosmo_to_addr_formatter_type(&a.zone_type) {
                place[addr_equivalent] = Some(a.name.clone());
            }
        }
        place
    }
}

fn cosmo_to_addr_formatter_type(
    cosmo_type: &Option<cosmogony::ZoneType>,
) -> Option<address_formatter::Component> {
    use address_formatter::Component;
    match cosmo_type {
        Some(cosmogony::ZoneType::City) => Some(Component::City),
        Some(cosmogony::ZoneType::Country) => Some(Component::Country),
        Some(cosmogony::ZoneType::State) => Some(Component::State),
        Some(cosmogony::ZoneType::Suburb) => Some(Component::Suburb),
        // not sure, but it seems a cosmogony::StateDistrict is a County in address_formatter
        Some(cosmogony::ZoneType::StateDistrict) => Some(Component::County),
        _ => None,
    }
}

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

    fn get_nl_admins() -> Vec<mimir::Admin> {
        vec![
            mimir::Admin {
                id: "admin:amsterdam".to_string(),
                level: 10,
                name: "Amsterdam".to_string(),
                label: "Amsterdam, Noord-Hollad, Nederland".to_string(),
                zone_type: Some(ZoneType::City),
                ..Default::default()
            },
            mimir::Admin {
                id: "admin:noord-holland".to_string(),
                level: 4,
                name: "Noordh-Holland".to_string(),
                label: "Noord-Hollad, Nederland".to_string(),
                zone_type: Some(ZoneType::State),
                ..Default::default()
            },
            mimir::Admin {
                id: "admin:Nederland".to_string(),
                level: 2,
                name: "Nederland".to_string(),
                label: "Nederland".to_string(),
                zone_type: Some(ZoneType::Country),
                ..Default::default()
            },
        ]
    }

    fn get_fr_admins() -> Vec<mimir::Admin> {
        vec![
            mimir::Admin {
                id: "admin:paris".to_string(),
                level: 8,
                name: "Paris".to_string(),
                label: "Paris (75000-75116), Île-de-France, France".to_string(),
                zone_type: Some(ZoneType::City),
                ..Default::default()
            },
            mimir::Admin {
                id: "admin:idf".to_string(),
                level: 4,
                name: "Île-de-France".to_string(),
                label: "Île-de-France, France".to_string(),
                zone_type: Some(ZoneType::State),
                ..Default::default()
            },
            mimir::Admin {
                id: "admin:france".to_string(),
                level: 2,
                name: "France".to_string(),
                label: "France".to_string(),
                zone_type: Some(ZoneType::Country),
                ..Default::default()
            },
        ]
    }

    #[test]
    fn nl_addr() {
        let (name, label) = format_addr_name_and_label(
            "573",
            "Herengracht",
            get_nl_admins().iter(),
            &vec!["nl".to_owned()],
        );
        assert_eq!(name, "Herengracht 573");
        assert_eq!(label, "Herengracht 573 (Amsterdam)");
    }
    #[test]
    fn nl_street() {
        let label = format_street_label(
            "Herengracht",
            get_nl_admins().iter(),
            &vec!["nl".to_owned()],
        );
        assert_eq!(label, "Herengracht (Amsterdam)");
    }
    #[test]
    fn nl_poi() {
        let label = format_poi_label(
            "Delirium Cafe",
            get_nl_admins().iter(),
            &vec!["nl".to_owned()],
        );
        assert_eq!(label, "Delirium Cafe (Amsterdam)");
    }

    #[test]
    fn fr_addr() {
        let (name, label) = format_addr_name_and_label(
            "20",
            "rue hector malot",
            get_fr_admins().iter(),
            &vec!["fr".to_owned()],
        );
        assert_eq!(name, "20 rue hector malot");
        assert_eq!(label, "20 rue hector malot (Paris)");
    }
    #[test]
    fn fr_street() {
        let label = format_street_label(
            "rue hector malot",
            get_fr_admins().iter(),
            &vec!["fr".to_owned()],
        );
        assert_eq!(label, "rue hector malot (Paris)");
    }
    #[test]
    fn fr_poi() {
        let label = format_poi_label("Le Rossli", get_fr_admins().iter(), &vec!["fr".to_owned()]);
        assert_eq!(label, "Le Rossli (Paris)");
    }
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::{Constraint, Expression, Solver, Variable};
use cassowary::WeightedRelation::*;
use cassowary::strength::{REQUIRED, WEAK};

use terminal::Terminal;
use backend::Backend;

#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height,
        }
    }

    pub fn area(&self) -> u16 {
        self.width * self.height
    }

    pub fn left(&self) -> u16 {
        self.x
    }

    pub fn right(&self) -> u16 {
        self.x + self.width
    }

    pub fn top(&self) -> u16 {
        self.y
    }

    pub fn bottom(&self) -> u16 {
        self.y + self.height
    }

    pub fn inner(&self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(&self, other: &Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(&self, other: &Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(&self, other: &Rect) -> bool {
        self.x < other.x + other.width && self.x + self.width > other.x
            && self.y < other.y + other.height && self.y + self.height > other.y
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Size {
    Fixed(u16),
    Percent(u16),
    Max(u16),
    Min(u16),
}

/// Wrapper function around the cassowary-rs solver to be able to split a given
/// area into smaller ones based on the preferred widths or heights and the direction.
///
/// # Examples
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Size, Direction, split};
///
/// # fn main() {
///     let chunks = split(&Rect{x: 2, y: 2, width: 10, height: 10},
///                        &Direction::Vertical,
///                        0,
///                        &[Size::Fixed(5), Size::Min(0)]);
///     assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
///                             Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = sizes
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
    let dest_area = area.inner(margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut constraints: Vec<Constraint> = Vec::with_capacity(elements.len() * 4 + sizes.len() * 6);
    for elt in &elements {
        constraints.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        constraints.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        constraints.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        constraints.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        constraints.push(match *dir {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        constraints.push(match *dir {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match *dir {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                constraints.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Size::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                constraints.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Size::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&constraints).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = value as u16;
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }
    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match *dir {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// Describes a layout and may be used to group widgets in a specific area of the terminal
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// use tui::layout::{Group, Direction, Size};
/// # fn main() {
///     Group::default()
///         .direction(Direction::Vertical)
///         .margin(0)
///         .sizes(&[Size::Percent(50), Size::Percent(50)]);
/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Group {
    pub direction: Direction,
    pub margin: u16,
    pub sizes: Vec<Size>,
}

impl Default for Group {
    fn default() -> Group {
        Group {
            direction: Direction::Horizontal,
            margin: 0,
            sizes: Vec::new(),
        }
    }
}

impl Group {
    pub fn direction(&mut self, direction: Direction) -> &mut Group {
        self.direction = direction;
        self
    }

    pub fn margin(&mut self, margin: u16) -> &mut Group {
        self.margin = margin;
        self
    }

    pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
        self.sizes = Vec::from(sizes);
        self
    }
    pub fn render<F, B>(&self, t: &mut Terminal<B>, area: &Rect, mut f: F)
    where
        B: Backend,
        F: FnMut(&mut Terminal<B>, &[Rect]),
    {
        let chunks = t.compute_layout(self, area);
        f(t, &chunks);
    }
}
<commit_msg>Add Copy trait for layout enums<commit_after>use std::cmp::{max, min};
use std::collections::HashMap;

use cassowary::{Constraint, Expression, Solver, Variable};
use cassowary::WeightedRelation::*;
use cassowary::strength::{REQUIRED, WEAK};

use terminal::Terminal;
use backend::Backend;

#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    Horizontal,
    Vertical,
}

/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl Default for Rect {
    fn default() -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
        }
    }
}

impl Rect {
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            width: width,
            height: height,
        }
    }

    pub fn area(&self) -> u16 {
        self.width * self.height
    }

    pub fn left(&self) -> u16 {
        self.x
    }

    pub fn right(&self) -> u16 {
        self.x + self.width
    }

    pub fn top(&self) -> u16 {
        self.y
    }

    pub fn bottom(&self) -> u16 {
        self.y + self.height
    }

    pub fn inner(&self, margin: u16) -> Rect {
        if self.width < 2 * margin || self.height < 2 * margin {
            Rect::default()
        } else {
            Rect {
                x: self.x + margin,
                y: self.y + margin,
                width: self.width - 2 * margin,
                height: self.height - 2 * margin,
            }
        }
    }

    pub fn union(&self, other: &Rect) -> Rect {
        let x1 = min(self.x, other.x);
        let y1 = min(self.y, other.y);
        let x2 = max(self.x + self.width, other.x + other.width);
        let y2 = max(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersection(&self, other: &Rect) -> Rect {
        let x1 = max(self.x, other.x);
        let y1 = max(self.y, other.y);
        let x2 = min(self.x + self.width, other.x + other.width);
        let y2 = min(self.y + self.height, other.y + other.height);
        Rect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    pub fn intersects(&self, other: &Rect) -> bool {
        self.x < other.x + other.width && self.x + self.width > other.x
            && self.y < other.y + other.height && self.y + self.height > other.y
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Size {
    Fixed(u16),
    Percent(u16),
    Max(u16),
    Min(u16),
}

/// Wrapper function around the cassowary-rs solver to be able to split a given
/// area into smaller ones based on the preferred widths or heights and the direction.
///
/// # Examples
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Size, Direction, split};
///
/// # fn main() {
///     let chunks = split(&Rect{x: 2, y: 2, width: 10, height: 10},
///                        &Direction::Vertical,
///                        0,
///                        &[Size::Fixed(5), Size::Min(0)]);
///     assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
///                             Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
    let mut solver = Solver::new();
    let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
    let elements = sizes
        .iter()
        .map(|_| Element::new())
        .collect::<Vec<Element>>();
    let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
    let dest_area = area.inner(margin);
    for (i, e) in elements.iter().enumerate() {
        vars.insert(e.x, (i, 0));
        vars.insert(e.y, (i, 1));
        vars.insert(e.width, (i, 2));
        vars.insert(e.height, (i, 3));
    }
    let mut constraints: Vec<Constraint> = Vec::with_capacity(elements.len() * 4 + sizes.len() * 6);
    for elt in &elements {
        constraints.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
        constraints.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
        constraints.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
        constraints.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
    }
    if let Some(first) = elements.first() {
        constraints.push(match *dir {
            Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
            Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
        });
    }
    if let Some(last) = elements.last() {
        constraints.push(match *dir {
            Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
            Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
        });
    }
    match *dir {
        Direction::Horizontal => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
                constraints.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].width | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
                    }
                    Size::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
                });
            }
        }
        Direction::Vertical => {
            for pair in elements.windows(2) {
                constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
            }
            for (i, size) in sizes.iter().enumerate() {
                constraints.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
                constraints.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
                constraints.push(match *size {
                    Size::Fixed(v) => elements[i].height | EQ(WEAK) | f64::from(v),
                    Size::Percent(v) => {
                        elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
                    }
                    Size::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
                    Size::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
                });
            }
        }
    }
    solver.add_constraints(&constraints).unwrap();
    for &(var, value) in solver.fetch_changes() {
        let (index, attr) = vars[&var];
        let value = value as u16;
        match attr {
            0 => {
                results[index].x = value;
            }
            1 => {
                results[index].y = value;
            }
            2 => {
                results[index].width = value;
            }
            3 => {
                results[index].height = value;
            }
            _ => {}
        }
    }
    // Fix imprecision by extending the last item a bit if necessary
    if let Some(last) = results.last_mut() {
        match *dir {
            Direction::Vertical => {
                last.height = dest_area.bottom() - last.y;
            }
            Direction::Horizontal => {
                last.width = dest_area.right() - last.x;
            }
        }
    }
    results
}

/// A container used by the solver inside split
struct Element {
    x: Variable,
    y: Variable,
    width: Variable,
    height: Variable,
}

impl Element {
    fn new() -> Element {
        Element {
            x: Variable::new(),
            y: Variable::new(),
            width: Variable::new(),
            height: Variable::new(),
        }
    }

    fn left(&self) -> Variable {
        self.x
    }

    fn top(&self) -> Variable {
        self.y
    }

    fn right(&self) -> Expression {
        self.x + self.width
    }

    fn bottom(&self) -> Expression {
        self.y + self.height
    }
}

/// Describes a layout and may be used to group widgets in a specific area of the terminal
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// use tui::layout::{Group, Direction, Size};
/// # fn main() {
///     Group::default()
///         .direction(Direction::Vertical)
///         .margin(0)
///         .sizes(&[Size::Percent(50), Size::Percent(50)]);
/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Group {
    pub direction: Direction,
    pub margin: u16,
    pub sizes: Vec<Size>,
}

impl Default for Group {
    fn default() -> Group {
        Group {
            direction: Direction::Horizontal,
            margin: 0,
            sizes: Vec::new(),
        }
    }
}

impl Group {
    pub fn direction(&mut self, direction: Direction) -> &mut Group {
        self.direction = direction;
        self
    }

    pub fn margin(&mut self, margin: u16) -> &mut Group {
        self.margin = margin;
        self
    }

    pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
        self.sizes = Vec::from(sizes);
        self
    }
    pub fn render<F, B>(&self, t: &mut Terminal<B>, area: &Rect, mut f: F)
    where
        B: Backend,
        F: FnMut(&mut Terminal<B>, &[Rect]),
    {
        let chunks = t.compute_layout(self, area);
        f(t, &chunks);
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate walkdir;

use self::chrono::{Duration, UTC};
use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::borrow::Borrow;
use std::collections::{BTreeSet, VecDeque};
use std::ffi::OsString;
use std::fs::{self, DirEntry};
use std::io::Error as IoError;
use std::os::unix::fs::MetadataExt;
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => ({
        let mut path = PathBuf::new();
        $(path.push($x);)+
        path
    });
}

pub fn trim_current_dir_prefix<S: AsRef<str>>(path_name: S) -> String {
    let pattern = format!(".{}", MAIN_SEPARATOR);

    path_name
        .as_ref()
        .trim_left_matches(&pattern)
        .to_string()
}

pub fn ls<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .map(|d| d.file_name())
        .map(OsString::into_string)
        .filter_map(Result::ok)
        .collect::<Vec<String>>();
    Ok(dirs)
}

pub fn is_empty_dir<P: AsRef<Path>>(path: P) -> bool {
    match fs::read_dir(path) {
        Ok(rd) => rd.count() == 0,
        Err(_) => false,
    }
}

pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(p: P, moratorium: D) -> bool {
    let threshold = UTC::now() - *moratorium.borrow();

    let accessed_time = match p.as_ref().metadata() {
        Ok(m)  => m.atime(),
        Err(_) => unreachable!("Wrong to use this function!!"),
    };

    accessed_time > threshold.timestamp()
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}

fn is_hidden(entry: &WalkDirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map_or(false, is_hidden_name)
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(trim_current_dir_prefix))
        .collect::<BTreeSet<String>>()
}

pub fn potential_empty_dirs<P: AsRef<Path>>(root: P) -> Result<BTreeSet<PathBuf>, IoError> {
    fn potential_empty_dirs(mut result: BTreeSet<PathBuf>, mut target_dirs: VecDeque<PathBuf>) -> Result<BTreeSet<PathBuf>, IoError> {
        match target_dirs.pop_front() {
            None => Ok(result),
            Some(mut target_dir) => {
                let entries = try!(fs::read_dir(&target_dir))
                    .filter_map(Result::ok)
                    .collect::<Vec<DirEntry>>();

                let include_file = entries
                    .iter()
                    .any(|entry| entry
                        .file_type()
                        .map(|t| t.is_file())
                        .unwrap_or(true)
                    );
                if include_file {
                    loop {
                        if !(result.remove(&target_dir) && target_dir.pop()) {
                            break;
                        }
                    }
                }

                let dirs = entries
                    .into_iter()
                    .filter_map(|entry| {
                        let is_dir = entry
                            .file_type()
                            .map(|t| t.is_dir())
                            .unwrap_or(false);
                        if is_dir {
                            Some(entry.path())
                        } else {
                            None
                        }
                    })
                    .collect::<BTreeSet<PathBuf>>();
                for dir in dirs.clone().into_iter() {
                    result.insert(dir);
                }

                let mut dirs = dirs.into_iter().collect::<VecDeque<PathBuf>>();
                target_dirs.append(&mut dirs);

                potential_empty_dirs(result, target_dirs)
            },
        }
    }

    let mut result     : BTreeSet<PathBuf> = BTreeSet::new();
    let mut target_dirs: VecDeque<PathBuf> = VecDeque::new();

    let root = root.as_ref().to_path_buf();
    result.insert(root.clone());
    target_dirs.push_back(root);

    potential_empty_dirs(result, target_dirs)
}

pub fn dirs_ordered_by_descending_depth<P: AsRef<Path>>(root: P) -> Vec<PathBuf> {
    let mut walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_entry(|e| e.file_type().is_dir())
        .filter_map(Result::ok)
        .collect::<Vec<WalkDirEntry>>();

    walker.sort_by(|l, r| {
        let l_depth = &l.depth();
        let r_depth = &r.depth();
        r_depth.cmp(l_depth)
    });

    walker
        .into_iter()
        .map(|e| e.path().to_path_buf())
        .collect::<Vec<PathBuf>>()
}

#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

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

    use std::collections::BTreeSet;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    struct Helper {
        d1: String,
        d2: String,
        d3: String,
        f1: String,
        f2: String,
    }
    impl Helper {
        fn new(suffix: &str) -> Self {
            Helper {
                d1: format!("directory1_{}", suffix),
                d2: format!("directory2_{}", suffix),
                d3: format!("directory3_{}", suffix),
                f1: format!("file1_{}"     , suffix),
                f2: format!("file2_{}"     , suffix),
            }
        }

        fn path_to_d1(&self) -> PathBuf {
            PathBuf::new().join(&self.d1)
        }
        fn path_to_d2(&self) -> PathBuf {
            self.path_to_d1().join(&self.d2)
        }
        fn path_to_d3(&self) -> PathBuf {
            self.path_to_d2().join(&self.d3)
        }

        fn path_to_f1(&self) -> PathBuf {
            self.path_to_d1().join(&self.f1)
        }
        fn path_to_f2(&self) -> PathBuf {
            self.path_to_d2().join(&self.f2)
        }

        fn remove_dirs_and_files(&self) {
            fs::remove_dir_all(self.path_to_d1()).ok();
        }

        fn create_dirs_and_files(&self) {
            self.remove_dirs_and_files();

            fs::create_dir_all(self.path_to_d3()).ok();

            let mut f = File::create(self.path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f2()).ok();
        }

        fn to_string_forcely(path: PathBuf) -> String {
            path.to_str().unwrap().to_string()
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn trim_current_dir_prefix_should_return_trimmed() {
        let path_names = [
            (""   , "./"    ),
            (""   , "././"  ),
            (""   , "./././"),
            ("a"  , "./a"   ),
            ("a/" , "./a/"  ),
            ("a/b", "./a/b" ),
        ];
        for &(ref correct, ref input) in &path_names {
            assert_eq!(correct.to_string(), trim_current_dir_prefix(input));
        }
    }
    #[test]
    fn trim_current_dir_prefix_should_return_as_it_is() {
        let path_names = [
            "",
            "/",
            "/a",
            "/a/b",
            "a",
            "a/",
            "a/b",
        ];
        for path_name in &path_names {
            assert_eq!(path_name.to_string(), trim_current_dir_prefix(path_name));
        }
    }

    #[test]
    fn ls_should_return_ok() {
        let helper = Helper::new("ls_Ok");
        helper.create_dirs_and_files();

        let empty_vec: Vec<String> = Vec::new();

        assert_eq!(vec![helper.d2.clone(), helper.f1.clone()], ls(helper.path_to_d1()).unwrap());
        assert_eq!(vec![helper.d3.clone(), helper.f2.clone()], ls(helper.path_to_d2()).unwrap());
        assert_eq!(empty_vec                                 , ls(helper.path_to_d3()).unwrap());

        helper.remove_dirs_and_files();
    }

    #[test]
    fn is_empty_dir_should_return_true() {
        let helper = Helper::new("is_empty_dir_true");
        helper.create_dirs_and_files();

        assert!(is_empty_dir(helper.path_to_d3()));

        helper.remove_dirs_and_files();
    }
    #[test]
    fn is_empty_dir_should_return_false() {
        let helper = Helper::new("is_empty_dir_false");
        helper.create_dirs_and_files();

        assert!(!is_empty_dir(helper.path_to_d1()));
        assert!(!is_empty_dir(helper.path_to_d2()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
        let helper = Helper::new("walk_dir_BTreeSet");
        helper.create_dirs_and_files();

        let mut correct = BTreeSet::new();
        correct.insert(Helper::to_string_forcely(helper.path_to_f1()));
        correct.insert(Helper::to_string_forcely(helper.path_to_f2()));

        assert_eq!(correct, walk_dir(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn dirs_ordered_by_descending_depth_should_return_vec() {
        let helper = Helper::new("dirs_ordered_by_descending_depth_Vec");
        helper.create_dirs_and_files();

        let correct = vec![helper.path_to_d3(), helper.path_to_d2(), helper.path_to_d1()];

        assert_eq!(correct, dirs_ordered_by_descending_depth(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }
}
<commit_msg>[Refactoring] Rename to potentially_empty_dirs()<commit_after>extern crate chrono;
extern crate walkdir;

use self::chrono::{Duration, UTC};
use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::borrow::Borrow;
use std::collections::{BTreeSet, VecDeque};
use std::ffi::OsString;
use std::fs::{self, DirEntry};
use std::io::Error as IoError;
use std::os::unix::fs::MetadataExt;
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => ({
        let mut path = PathBuf::new();
        $(path.push($x);)+
        path
    });
}

pub fn trim_current_dir_prefix<S: AsRef<str>>(path_name: S) -> String {
    let pattern = format!(".{}", MAIN_SEPARATOR);

    path_name
        .as_ref()
        .trim_left_matches(&pattern)
        .to_string()
}

pub fn ls<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .map(|d| d.file_name())
        .map(OsString::into_string)
        .filter_map(Result::ok)
        .collect::<Vec<String>>();
    Ok(dirs)
}

pub fn is_empty_dir<P: AsRef<Path>>(path: P) -> bool {
    match fs::read_dir(path) {
        Ok(rd) => rd.count() == 0,
        Err(_) => false,
    }
}

pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(p: P, moratorium: D) -> bool {
    let threshold = UTC::now() - *moratorium.borrow();

    let accessed_time = match p.as_ref().metadata() {
        Ok(m)  => m.atime(),
        Err(_) => unreachable!("Wrong to use this function!!"),
    };

    accessed_time > threshold.timestamp()
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}

fn is_hidden(entry: &WalkDirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map_or(false, is_hidden_name)
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(trim_current_dir_prefix))
        .collect::<BTreeSet<String>>()
}

pub fn potentially_empty_dirs<P: AsRef<Path>>(root: P) -> Result<BTreeSet<PathBuf>, IoError> {
    fn potentially_empty_dirs(mut result: BTreeSet<PathBuf>, mut target_dirs: VecDeque<PathBuf>) -> Result<BTreeSet<PathBuf>, IoError> {
        match target_dirs.pop_front() {
            None => Ok(result),
            Some(mut target_dir) => {
                let entries = try!(fs::read_dir(&target_dir))
                    .filter_map(Result::ok)
                    .collect::<Vec<DirEntry>>();

                let include_file = entries
                    .iter()
                    .any(|entry| entry
                        .file_type()
                        .map(|t| t.is_file())
                        .unwrap_or(true)
                    );
                if include_file {
                    loop {
                        if !(result.remove(&target_dir) && target_dir.pop()) {
                            break;
                        }
                    }
                }

                let dirs = entries
                    .into_iter()
                    .filter_map(|entry| {
                        let is_dir = entry
                            .file_type()
                            .map(|t| t.is_dir())
                            .unwrap_or(false);
                        if is_dir {
                            Some(entry.path())
                        } else {
                            None
                        }
                    })
                    .collect::<BTreeSet<PathBuf>>();
                for dir in dirs.clone().into_iter() {
                    result.insert(dir);
                }

                let mut dirs = dirs.into_iter().collect::<VecDeque<PathBuf>>();
                target_dirs.append(&mut dirs);

                potentially_empty_dirs(result, target_dirs)
            },
        }
    }

    let mut result     : BTreeSet<PathBuf> = BTreeSet::new();
    let mut target_dirs: VecDeque<PathBuf> = VecDeque::new();

    let root = root.as_ref().to_path_buf();
    result.insert(root.clone());
    target_dirs.push_back(root);

    potentially_empty_dirs(result, target_dirs)
}

pub fn dirs_ordered_by_descending_depth<P: AsRef<Path>>(root: P) -> Vec<PathBuf> {
    let mut walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_entry(|e| e.file_type().is_dir())
        .filter_map(Result::ok)
        .collect::<Vec<WalkDirEntry>>();

    walker.sort_by(|l, r| {
        let l_depth = &l.depth();
        let r_depth = &r.depth();
        r_depth.cmp(l_depth)
    });

    walker
        .into_iter()
        .map(|e| e.path().to_path_buf())
        .collect::<Vec<PathBuf>>()
}

#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

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

    use std::collections::BTreeSet;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    struct Helper {
        d1: String,
        d2: String,
        d3: String,
        f1: String,
        f2: String,
    }
    impl Helper {
        fn new(suffix: &str) -> Self {
            Helper {
                d1: format!("directory1_{}", suffix),
                d2: format!("directory2_{}", suffix),
                d3: format!("directory3_{}", suffix),
                f1: format!("file1_{}"     , suffix),
                f2: format!("file2_{}"     , suffix),
            }
        }

        fn path_to_d1(&self) -> PathBuf {
            PathBuf::new().join(&self.d1)
        }
        fn path_to_d2(&self) -> PathBuf {
            self.path_to_d1().join(&self.d2)
        }
        fn path_to_d3(&self) -> PathBuf {
            self.path_to_d2().join(&self.d3)
        }

        fn path_to_f1(&self) -> PathBuf {
            self.path_to_d1().join(&self.f1)
        }
        fn path_to_f2(&self) -> PathBuf {
            self.path_to_d2().join(&self.f2)
        }

        fn remove_dirs_and_files(&self) {
            fs::remove_dir_all(self.path_to_d1()).ok();
        }

        fn create_dirs_and_files(&self) {
            self.remove_dirs_and_files();

            fs::create_dir_all(self.path_to_d3()).ok();

            let mut f = File::create(self.path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f2()).ok();
        }

        fn to_string_forcely(path: PathBuf) -> String {
            path.to_str().unwrap().to_string()
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn trim_current_dir_prefix_should_return_trimmed() {
        let path_names = [
            (""   , "./"    ),
            (""   , "././"  ),
            (""   , "./././"),
            ("a"  , "./a"   ),
            ("a/" , "./a/"  ),
            ("a/b", "./a/b" ),
        ];
        for &(ref correct, ref input) in &path_names {
            assert_eq!(correct.to_string(), trim_current_dir_prefix(input));
        }
    }
    #[test]
    fn trim_current_dir_prefix_should_return_as_it_is() {
        let path_names = [
            "",
            "/",
            "/a",
            "/a/b",
            "a",
            "a/",
            "a/b",
        ];
        for path_name in &path_names {
            assert_eq!(path_name.to_string(), trim_current_dir_prefix(path_name));
        }
    }

    #[test]
    fn ls_should_return_ok() {
        let helper = Helper::new("ls_Ok");
        helper.create_dirs_and_files();

        let empty_vec: Vec<String> = Vec::new();

        assert_eq!(vec![helper.d2.clone(), helper.f1.clone()], ls(helper.path_to_d1()).unwrap());
        assert_eq!(vec![helper.d3.clone(), helper.f2.clone()], ls(helper.path_to_d2()).unwrap());
        assert_eq!(empty_vec                                 , ls(helper.path_to_d3()).unwrap());

        helper.remove_dirs_and_files();
    }

    #[test]
    fn is_empty_dir_should_return_true() {
        let helper = Helper::new("is_empty_dir_true");
        helper.create_dirs_and_files();

        assert!(is_empty_dir(helper.path_to_d3()));

        helper.remove_dirs_and_files();
    }
    #[test]
    fn is_empty_dir_should_return_false() {
        let helper = Helper::new("is_empty_dir_false");
        helper.create_dirs_and_files();

        assert!(!is_empty_dir(helper.path_to_d1()));
        assert!(!is_empty_dir(helper.path_to_d2()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
        let helper = Helper::new("walk_dir_BTreeSet");
        helper.create_dirs_and_files();

        let mut correct = BTreeSet::new();
        correct.insert(Helper::to_string_forcely(helper.path_to_f1()));
        correct.insert(Helper::to_string_forcely(helper.path_to_f2()));

        assert_eq!(correct, walk_dir(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn dirs_ordered_by_descending_depth_should_return_vec() {
        let helper = Helper::new("dirs_ordered_by_descending_depth_Vec");
        helper.create_dirs_and_files();

        let correct = vec![helper.path_to_d3(), helper.path_to_d2(), helper.path_to_d1()];

        assert_eq!(correct, dirs_ordered_by_descending_depth(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Seek};
use std::path::Path;
use std::u32;

use crate::codecs::*;

use crate::dynimage::DynamicImage;
use crate::error::{ImageError, ImageFormatHint, ImageResult};
use crate::image;
use crate::image::ImageFormat;
#[allow(unused_imports)] // When no features are supported
use crate::image::{ImageDecoder, ImageEncoder};
use crate::{
    color,
    error::{UnsupportedError, UnsupportedErrorKind},
    ImageOutputFormat,
};

pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
    let buffered_read = BufReader::new(File::open(path).map_err(ImageError::IoError)?);

    load(buffered_read, ImageFormat::from_path(path)?)
}

/// Create a new image from a Reader.
///
/// Assumes the reader is already buffered. For optimal performance,
/// consider wrapping the reader with a `BufReader::new()`.
///
/// Try [`io::Reader`] for more advanced uses.
///
/// [`io::Reader`]: io/struct.Reader.html
#[allow(unused_variables)]
// r is unused if no features are supported.
pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
    load_inner(r, super::Limits::default(), format)
}

pub(crate) trait DecoderVisitor {
    type Result;
    fn visit_decoder<'a, D: ImageDecoder<'a>>(self, decoder: D) -> ImageResult<Self::Result>;
}

pub(crate) fn load_decoder<R: BufRead + Seek, V: DecoderVisitor>(
    r: R,
    format: ImageFormat,
    visitor: V,
) -> ImageResult<V::Result> {
    #[allow(unreachable_patterns)]
    // Default is unreachable if all features are supported.
    match format {
        #[cfg(feature = "avif-decoder")]
        image::ImageFormat::Avif => visitor.visit_decoder(avif::AvifDecoder::new(r)?),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => visitor.visit_decoder(png::PngDecoder::new(r)?),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => visitor.visit_decoder(gif::GifDecoder::new(r)?),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => visitor.visit_decoder(jpeg::JpegDecoder::new(r)?),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => visitor.visit_decoder(webp::WebPDecoder::new(r)?),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => visitor.visit_decoder(tiff::TiffDecoder::new(r)?),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => visitor.visit_decoder(tga::TgaDecoder::new(r)?),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => visitor.visit_decoder(dds::DdsDecoder::new(r)?),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => visitor.visit_decoder(bmp::BmpDecoder::new(r)?),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => visitor.visit_decoder(ico::IcoDecoder::new(r)?),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => visitor.visit_decoder(hdr::HdrAdapter::new(BufReader::new(r))?),
        #[cfg(feature = "openexr")]
        image::ImageFormat::OpenExr => visitor.visit_decoder(openexr::OpenExrDecoder::new(r)?),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => visitor.visit_decoder(pnm::PnmDecoder::new(r)?),
        #[cfg(feature = "farbfeld")]
        image::ImageFormat::Farbfeld => visitor.visit_decoder(farbfeld::FarbfeldDecoder::new(r)?),
        _ => Err(ImageError::Unsupported(
            ImageFormatHint::Exact(format).into(),
        )),
    }
}

pub(crate) fn load_inner<R: BufRead + Seek>(
    r: R,
    limits: super::Limits,
    format: ImageFormat,
) -> ImageResult<DynamicImage> {
    struct LoadVisitor(super::Limits);

    impl DecoderVisitor for LoadVisitor {
        type Result = DynamicImage;

        fn visit_decoder<'a, D: ImageDecoder<'a>>(
            self,
            mut decoder: D,
        ) -> ImageResult<Self::Result> {
            let mut limits = self.0;
            // Check that we do not allocate a bigger buffer than we are allowed to
            // FIXME: should this rather go in `DynamicImage::from_decoder` somehow?
            limits.reserve(decoder.total_bytes())?;
            decoder.set_limits(limits)?;
            DynamicImage::from_decoder(decoder)
        }
    }

    load_decoder(r, format, LoadVisitor(limits))
}

pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
    let format = image::ImageFormat::from_path(path)?;
    let reader = BufReader::new(File::open(path)?);
    image_dimensions_with_format_impl(reader, format)
}

#[allow(unused_variables)]
// fin is unused if no features are supported.
pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(
    buffered_read: R,
    format: ImageFormat,
) -> ImageResult<(u32, u32)> {
    struct DimVisitor;

    impl DecoderVisitor for DimVisitor {
        type Result = (u32, u32);
        fn visit_decoder<'a, D: ImageDecoder<'a>>(self, decoder: D) -> ImageResult<Self::Result> {
            Ok(decoder.dimensions())
        }
    }

    load_decoder(buffered_read, format, DimVisitor)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
) -> ImageResult<()> {
    let format = ImageFormat::from_path(path)?;
    save_buffer_with_format_impl(path, buf, width, height, color, format)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_with_format_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageFormat,
) -> ImageResult<()> {
    let buffered_file_write = &mut BufWriter::new(File::create(path)?); // always seekable

    let format = match format {
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => {
            let ext = path
                .extension()
                .and_then(|s| s.to_str())
                .map_or("".to_string(), |s| s.to_ascii_lowercase());
            ImageOutputFormat::Pnm(match &*ext {
                "pbm" => pnm::PnmSubtype::Bitmap(pnm::SampleEncoding::Binary),
                "pgm" => pnm::PnmSubtype::Graymap(pnm::SampleEncoding::Binary),
                "ppm" => pnm::PnmSubtype::Pixmap(pnm::SampleEncoding::Binary),
                "pam" => pnm::PnmSubtype::ArbitraryMap,
                _ => {
                    return Err(ImageError::Unsupported(
                        ImageFormatHint::Exact(format).into(),
                    ))
                } // Unsupported Pnm subtype.
            })
        }
        // #[cfg(feature = "hdr")]
        // image::ImageFormat::Hdr => hdr::HdrEncoder::new(fout).encode(&[Rgb<f32>], width, height), // usize
        format => format.into(),
    };

    write_buffer_impl(buffered_file_write, buf, width, height, color, format)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn write_buffer_impl<W: std::io::Write + Seek>(
    buffered_write: &mut W,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageOutputFormat,
) -> ImageResult<()> {
    match format {
        #[cfg(feature = "png")]
        ImageOutputFormat::Png => {
            png::PngEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "jpeg")]
        ImageOutputFormat::Jpeg(quality) => {
            jpeg::JpegEncoder::new_with_quality(buffered_write, quality)
                .write_image(buf, width, height, color)
        }
        #[cfg(feature = "pnm")]
        ImageOutputFormat::Pnm(subtype) => pnm::PnmEncoder::new(buffered_write)
            .with_subtype(subtype)
            .write_image(buf, width, height, color),
        #[cfg(feature = "gif")]
        ImageOutputFormat::Gif => {
            gif::GifEncoder::new(buffered_write).encode(buf, width, height, color)
        }
        #[cfg(feature = "ico")]
        ImageOutputFormat::Ico => {
            ico::IcoEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "bmp")]
        ImageOutputFormat::Bmp => {
            bmp::BmpEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "farbfeld")]
        ImageOutputFormat::Farbfeld => {
            farbfeld::FarbfeldEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "tga")]
        ImageOutputFormat::Tga => {
            tga::TgaEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "openexr")]
        ImageOutputFormat::OpenExr => {
            openexr::OpenExrEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "tiff")]
        ImageOutputFormat::Tiff => {
            tiff::TiffEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "avif-encoder")]
        ImageOutputFormat::Avif => {
            avif::AvifEncoder::new(buffered_write).write_image(buf, width, height, color)
        }

        image::ImageOutputFormat::Unsupported(msg) => Err(ImageError::Unsupported(
            UnsupportedError::from_format_and_kind(
                ImageFormatHint::Unknown,
                UnsupportedErrorKind::Format(ImageFormatHint::Name(msg)),
            ),
        )),
    }
}

static MAGIC_BYTES: [(&[u8], ImageFormat); 21] = [
    (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
    (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
    (b"GIF89a", ImageFormat::Gif),
    (b"GIF87a", ImageFormat::Gif),
    (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
    (b"MM\x00*", ImageFormat::Tiff),
    (b"II*\x00", ImageFormat::Tiff),
    (b"DDS ", ImageFormat::Dds),
    (b"BM", ImageFormat::Bmp),
    (&[0, 0, 1, 0], ImageFormat::Ico),
    (b"#?RADIANCE", ImageFormat::Hdr),
    (b"P1", ImageFormat::Pnm),
    (b"P2", ImageFormat::Pnm),
    (b"P3", ImageFormat::Pnm),
    (b"P4", ImageFormat::Pnm),
    (b"P5", ImageFormat::Pnm),
    (b"P6", ImageFormat::Pnm),
    (b"P7", ImageFormat::Pnm),
    (b"farbfeld", ImageFormat::Farbfeld),
    (b"\0\0\0 ftypavif", ImageFormat::Avif),
    (&[0x76, 0x2f, 0x31, 0x01], ImageFormat::OpenExr), // = &exr::meta::magic_number::BYTES
];

/// Guess image format from memory block
///
/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
/// TGA is not supported by this function.
/// This is not to be trusted on the validity of the whole memory block
pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
    match guess_format_impl(buffer) {
        Some(format) => Ok(format),
        None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
    }
}

pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
    for &(signature, format) in &MAGIC_BYTES {
        if buffer.starts_with(signature) {
            return Some(format);
        }
    }

    None
}
<commit_msg>Add new magic byte<commit_after>use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Seek};
use std::path::Path;
use std::u32;

use crate::codecs::*;

use crate::dynimage::DynamicImage;
use crate::error::{ImageError, ImageFormatHint, ImageResult};
use crate::image;
use crate::image::ImageFormat;
#[allow(unused_imports)] // When no features are supported
use crate::image::{ImageDecoder, ImageEncoder};
use crate::{
    color,
    error::{UnsupportedError, UnsupportedErrorKind},
    ImageOutputFormat,
};

pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
    let buffered_read = BufReader::new(File::open(path).map_err(ImageError::IoError)?);

    load(buffered_read, ImageFormat::from_path(path)?)
}

/// Create a new image from a Reader.
///
/// Assumes the reader is already buffered. For optimal performance,
/// consider wrapping the reader with a `BufReader::new()`.
///
/// Try [`io::Reader`] for more advanced uses.
///
/// [`io::Reader`]: io/struct.Reader.html
#[allow(unused_variables)]
// r is unused if no features are supported.
pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
    load_inner(r, super::Limits::default(), format)
}

pub(crate) trait DecoderVisitor {
    type Result;
    fn visit_decoder<'a, D: ImageDecoder<'a>>(self, decoder: D) -> ImageResult<Self::Result>;
}

pub(crate) fn load_decoder<R: BufRead + Seek, V: DecoderVisitor>(
    r: R,
    format: ImageFormat,
    visitor: V,
) -> ImageResult<V::Result> {
    #[allow(unreachable_patterns)]
    // Default is unreachable if all features are supported.
    match format {
        #[cfg(feature = "avif-decoder")]
        image::ImageFormat::Avif => visitor.visit_decoder(avif::AvifDecoder::new(r)?),
        #[cfg(feature = "png")]
        image::ImageFormat::Png => visitor.visit_decoder(png::PngDecoder::new(r)?),
        #[cfg(feature = "gif")]
        image::ImageFormat::Gif => visitor.visit_decoder(gif::GifDecoder::new(r)?),
        #[cfg(feature = "jpeg")]
        image::ImageFormat::Jpeg => visitor.visit_decoder(jpeg::JpegDecoder::new(r)?),
        #[cfg(feature = "webp")]
        image::ImageFormat::WebP => visitor.visit_decoder(webp::WebPDecoder::new(r)?),
        #[cfg(feature = "tiff")]
        image::ImageFormat::Tiff => visitor.visit_decoder(tiff::TiffDecoder::new(r)?),
        #[cfg(feature = "tga")]
        image::ImageFormat::Tga => visitor.visit_decoder(tga::TgaDecoder::new(r)?),
        #[cfg(feature = "dds")]
        image::ImageFormat::Dds => visitor.visit_decoder(dds::DdsDecoder::new(r)?),
        #[cfg(feature = "bmp")]
        image::ImageFormat::Bmp => visitor.visit_decoder(bmp::BmpDecoder::new(r)?),
        #[cfg(feature = "ico")]
        image::ImageFormat::Ico => visitor.visit_decoder(ico::IcoDecoder::new(r)?),
        #[cfg(feature = "hdr")]
        image::ImageFormat::Hdr => visitor.visit_decoder(hdr::HdrAdapter::new(BufReader::new(r))?),
        #[cfg(feature = "openexr")]
        image::ImageFormat::OpenExr => visitor.visit_decoder(openexr::OpenExrDecoder::new(r)?),
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => visitor.visit_decoder(pnm::PnmDecoder::new(r)?),
        #[cfg(feature = "farbfeld")]
        image::ImageFormat::Farbfeld => visitor.visit_decoder(farbfeld::FarbfeldDecoder::new(r)?),
        _ => Err(ImageError::Unsupported(
            ImageFormatHint::Exact(format).into(),
        )),
    }
}

pub(crate) fn load_inner<R: BufRead + Seek>(
    r: R,
    limits: super::Limits,
    format: ImageFormat,
) -> ImageResult<DynamicImage> {
    struct LoadVisitor(super::Limits);

    impl DecoderVisitor for LoadVisitor {
        type Result = DynamicImage;

        fn visit_decoder<'a, D: ImageDecoder<'a>>(
            self,
            mut decoder: D,
        ) -> ImageResult<Self::Result> {
            let mut limits = self.0;
            // Check that we do not allocate a bigger buffer than we are allowed to
            // FIXME: should this rather go in `DynamicImage::from_decoder` somehow?
            limits.reserve(decoder.total_bytes())?;
            decoder.set_limits(limits)?;
            DynamicImage::from_decoder(decoder)
        }
    }

    load_decoder(r, format, LoadVisitor(limits))
}

pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
    let format = image::ImageFormat::from_path(path)?;
    let reader = BufReader::new(File::open(path)?);
    image_dimensions_with_format_impl(reader, format)
}

#[allow(unused_variables)]
// fin is unused if no features are supported.
pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(
    buffered_read: R,
    format: ImageFormat,
) -> ImageResult<(u32, u32)> {
    struct DimVisitor;

    impl DecoderVisitor for DimVisitor {
        type Result = (u32, u32);
        fn visit_decoder<'a, D: ImageDecoder<'a>>(self, decoder: D) -> ImageResult<Self::Result> {
            Ok(decoder.dimensions())
        }
    }

    load_decoder(buffered_read, format, DimVisitor)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
) -> ImageResult<()> {
    let format = ImageFormat::from_path(path)?;
    save_buffer_with_format_impl(path, buf, width, height, color, format)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn save_buffer_with_format_impl(
    path: &Path,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageFormat,
) -> ImageResult<()> {
    let buffered_file_write = &mut BufWriter::new(File::create(path)?); // always seekable

    let format = match format {
        #[cfg(feature = "pnm")]
        image::ImageFormat::Pnm => {
            let ext = path
                .extension()
                .and_then(|s| s.to_str())
                .map_or("".to_string(), |s| s.to_ascii_lowercase());
            ImageOutputFormat::Pnm(match &*ext {
                "pbm" => pnm::PnmSubtype::Bitmap(pnm::SampleEncoding::Binary),
                "pgm" => pnm::PnmSubtype::Graymap(pnm::SampleEncoding::Binary),
                "ppm" => pnm::PnmSubtype::Pixmap(pnm::SampleEncoding::Binary),
                "pam" => pnm::PnmSubtype::ArbitraryMap,
                _ => {
                    return Err(ImageError::Unsupported(
                        ImageFormatHint::Exact(format).into(),
                    ))
                } // Unsupported Pnm subtype.
            })
        }
        // #[cfg(feature = "hdr")]
        // image::ImageFormat::Hdr => hdr::HdrEncoder::new(fout).encode(&[Rgb<f32>], width, height), // usize
        format => format.into(),
    };

    write_buffer_impl(buffered_file_write, buf, width, height, color, format)
}

#[allow(unused_variables)]
// Most variables when no features are supported
pub(crate) fn write_buffer_impl<W: std::io::Write + Seek>(
    buffered_write: &mut W,
    buf: &[u8],
    width: u32,
    height: u32,
    color: color::ColorType,
    format: ImageOutputFormat,
) -> ImageResult<()> {
    match format {
        #[cfg(feature = "png")]
        ImageOutputFormat::Png => {
            png::PngEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "jpeg")]
        ImageOutputFormat::Jpeg(quality) => {
            jpeg::JpegEncoder::new_with_quality(buffered_write, quality)
                .write_image(buf, width, height, color)
        }
        #[cfg(feature = "pnm")]
        ImageOutputFormat::Pnm(subtype) => pnm::PnmEncoder::new(buffered_write)
            .with_subtype(subtype)
            .write_image(buf, width, height, color),
        #[cfg(feature = "gif")]
        ImageOutputFormat::Gif => {
            gif::GifEncoder::new(buffered_write).encode(buf, width, height, color)
        }
        #[cfg(feature = "ico")]
        ImageOutputFormat::Ico => {
            ico::IcoEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "bmp")]
        ImageOutputFormat::Bmp => {
            bmp::BmpEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "farbfeld")]
        ImageOutputFormat::Farbfeld => {
            farbfeld::FarbfeldEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "tga")]
        ImageOutputFormat::Tga => {
            tga::TgaEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "openexr")]
        ImageOutputFormat::OpenExr => {
            openexr::OpenExrEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "tiff")]
        ImageOutputFormat::Tiff => {
            tiff::TiffEncoder::new(buffered_write).write_image(buf, width, height, color)
        }
        #[cfg(feature = "avif-encoder")]
        ImageOutputFormat::Avif => {
            avif::AvifEncoder::new(buffered_write).write_image(buf, width, height, color)
        }

        image::ImageOutputFormat::Unsupported(msg) => Err(ImageError::Unsupported(
            UnsupportedError::from_format_and_kind(
                ImageFormatHint::Unknown,
                UnsupportedErrorKind::Format(ImageFormatHint::Name(msg)),
            ),
        )),
    }
}

static MAGIC_BYTES: [(&[u8], ImageFormat); 22] = [
    (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
    (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
    (b"GIF89a", ImageFormat::Gif),
    (b"GIF87a", ImageFormat::Gif),
    (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
    (b"MM\x00*", ImageFormat::Tiff),
    (b"II*\x00", ImageFormat::Tiff),
    (b"DDS ", ImageFormat::Dds),
    (b"BM", ImageFormat::Bmp),
    (&[0, 0, 1, 0], ImageFormat::Ico),
    (b"#?RADIANCE", ImageFormat::Hdr),
    (b"P1", ImageFormat::Pnm),
    (b"P2", ImageFormat::Pnm),
    (b"P3", ImageFormat::Pnm),
    (b"P4", ImageFormat::Pnm),
    (b"P5", ImageFormat::Pnm),
    (b"P6", ImageFormat::Pnm),
    (b"P7", ImageFormat::Pnm),
    (b"farbfeld", ImageFormat::Farbfeld),
    (b"\0\0\0 ftypavif", ImageFormat::Avif),
    (b"\0\0\0\x1cftypavif", ImageFormat::Avif),
    (&[0x76, 0x2f, 0x31, 0x01], ImageFormat::OpenExr), // = &exr::meta::magic_number::BYTES
];

/// Guess image format from memory block
///
/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
/// TGA is not supported by this function.
/// This is not to be trusted on the validity of the whole memory block
pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
    match guess_format_impl(buffer) {
        Some(format) => Ok(format),
        None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
    }
}

pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
    for &(signature, format) in &MAGIC_BYTES {
        if buffer.starts_with(signature) {
            return Some(format);
        }
    }

    None
}
<|endoftext|>